FontCache: Replace DEPRECATED_DEFINE_STATIC_LOCAL with static NeverDestroyed<T>.
[WebKit-https.git] / Source / WebCore / platform / graphics / FontCache.cpp
1 /*
2  * Copyright (C) 2006, 2008, 2013 Apple Inc. All rights reserved.
3  * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "FontCache.h"
32
33 #include "Font.h"
34 #include "FontGlyphs.h"
35 #include "FontPlatformData.h"
36 #include "FontSelector.h"
37 #include "WebKitFontFamilyNames.h"
38 #include <wtf/HashMap.h>
39 #include <wtf/ListHashSet.h>
40 #include <wtf/NeverDestroyed.h>
41 #include <wtf/StdLibExtras.h>
42 #include <wtf/text/AtomicStringHash.h>
43 #include <wtf/text/StringHash.h>
44
45 #if ENABLE(OPENTYPE_VERTICAL)
46 #include "OpenTypeVerticalData.h"
47 #endif
48
49 #if PLATFORM(IOS)
50 #include "MemoryPressureHandler.h"
51 #include <wtf/Noncopyable.h>
52
53 // FIXME: We may be able to simplify this code using C++11 threading primitives, including std::call_once().
54 static pthread_mutex_t fontDataLock;
55
56 static void initFontCacheLockOnce()
57 {
58     pthread_mutexattr_t mutexAttribute;
59     pthread_mutexattr_init(&mutexAttribute);
60     pthread_mutexattr_settype(&mutexAttribute, PTHREAD_MUTEX_RECURSIVE);
61     pthread_mutex_init(&fontDataLock, &mutexAttribute);
62     pthread_mutexattr_destroy(&mutexAttribute);
63 }
64
65 static pthread_once_t initFontLockControl = PTHREAD_ONCE_INIT;
66
67 class FontLocker {
68     WTF_MAKE_NONCOPYABLE(FontLocker);
69 public:
70     FontLocker()
71     {
72         pthread_once(&initFontLockControl, initFontCacheLockOnce);
73         int lockcode = pthread_mutex_lock(&fontDataLock);
74         ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontDataLock lock failed with code:%d", lockcode);    
75     }
76     ~FontLocker()
77     {
78         int lockcode = pthread_mutex_unlock(&fontDataLock);
79         ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontDataLock unlock failed with code:%d", lockcode);
80     }
81 };
82 #endif // PLATFORM(IOS)
83
84 using namespace WTF;
85
86 namespace WebCore {
87
88 // FIXME: We should return a reference instead of a pointer since we never return a nullptr.
89 FontCache* fontCache()
90 {
91     static NeverDestroyed<FontCache> globalFontCache;
92     return &globalFontCache.get();
93 }
94
95 FontCache::FontCache()
96     : m_purgePreventCount(0)
97 {
98 }
99
100 struct FontPlatformDataCacheKey {
101     WTF_MAKE_FAST_ALLOCATED;
102 public:
103     FontPlatformDataCacheKey() { }
104     FontPlatformDataCacheKey(const AtomicString& family, const FontDescription& description)
105         : m_fontDescriptionKey(description)
106         , m_family(family)
107     { }
108
109     FontPlatformDataCacheKey(HashTableDeletedValueType) : m_fontDescriptionKey(hashTableDeletedSize()) { }
110     bool isHashTableDeletedValue() const { return m_fontDescriptionKey.size == hashTableDeletedSize(); }
111
112     bool operator==(const FontPlatformDataCacheKey& other) const
113     {
114         return equalIgnoringCase(m_family, other.m_family) && m_fontDescriptionKey == other.m_fontDescriptionKey;
115     }
116
117     FontDescriptionFontDataCacheKey m_fontDescriptionKey;
118     AtomicString m_family;
119
120 private:
121     static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; }
122 };
123
124 inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
125 {
126     return pairIntHash(CaseFoldingHash::hash(fontKey.m_family), fontKey.m_fontDescriptionKey.computeHash());
127 }
128
129 struct FontPlatformDataCacheKeyHash {
130     static unsigned hash(const FontPlatformDataCacheKey& font)
131     {
132         return computeHash(font);
133     }
134          
135     static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
136     {
137         return a == b;
138     }
139
140     static const bool safeToCompareToEmptyOrDeleted = true;
141 };
142
143 struct FontPlatformDataCacheKeyTraits : WTF::SimpleClassHashTraits<FontPlatformDataCacheKey> { };
144
145 typedef HashMap<FontPlatformDataCacheKey, OwnPtr<FontPlatformData>, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
146
147 static FontPlatformDataCache* gFontPlatformDataCache = 0;
148
149 static bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char* reference, unsigned length)
150 {
151     ASSERT(length > 0);
152     ASSERT(familyName.length() == length);
153     ASSERT(strlen(reference) == length);
154     const AtomicStringImpl* familyNameImpl = familyName.impl();
155     if (familyNameImpl->is8Bit())
156         return equalIgnoringCase(familyNameImpl->characters8(), reinterpret_cast<const LChar*>(reference), length);
157     return equalIgnoringCase(familyNameImpl->characters16(), reinterpret_cast<const LChar*>(reference), length);
158 }
159
160 template<size_t length>
161 static inline bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char (&reference)[length])
162 {
163     return familyNameEqualIgnoringCase(familyName, reference, length - 1);
164 }
165
166 static const AtomicString alternateFamilyName(const AtomicString& familyName)
167 {
168     // Alias Courier and Courier New.
169     // Alias Times and Times New Roman.
170     // Alias Arial and Helvetica.
171     switch (familyName.length()) {
172     case 5:
173         if (familyNameEqualIgnoringCase(familyName, "Arial"))
174             return AtomicString("Helvetica", AtomicString::ConstructFromLiteral);
175         if (familyNameEqualIgnoringCase(familyName, "Times"))
176             return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
177         break;
178     case 7:
179         if (familyNameEqualIgnoringCase(familyName, "Courier"))
180             return AtomicString("Courier New", AtomicString::ConstructFromLiteral);
181         break;
182     case 9:
183         if (familyNameEqualIgnoringCase(familyName, "Helvetica"))
184             return AtomicString("Arial", AtomicString::ConstructFromLiteral);
185         break;
186 #if !OS(WINDOWS)
187     // On Windows, Courier New (truetype font) is always present and
188     // Courier is a bitmap font. So, we don't want to map Courier New to
189     // Courier.
190     case 11:
191         if (familyNameEqualIgnoringCase(familyName, "Courier New"))
192             return AtomicString("Courier", AtomicString::ConstructFromLiteral);
193         break;
194 #endif // !OS(WINDOWS)
195     case 15:
196         if (familyNameEqualIgnoringCase(familyName, "Times New Roman"))
197             return AtomicString("Times", AtomicString::ConstructFromLiteral);
198         break;
199 #if OS(WINDOWS)
200     // On Windows, bitmap fonts are blocked altogether so that we have to 
201     // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font)
202     case 13:
203         if (familyNameEqualIgnoringCase(familyName, "MS Sans Serif"))
204             return AtomicString("Microsoft Sans Serif", AtomicString::ConstructFromLiteral);
205         break;
206
207     // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no 
208     // 'Microsoft Sans Serif-equivalent' for Serif.
209     case 8:
210         if (familyNameEqualIgnoringCase(familyName, "MS Serif"))
211             return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
212         break;
213 #endif // OS(WINDOWS)
214
215     }
216
217     return nullAtom;
218 }
219
220 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription,
221                                                        const AtomicString& passedFamilyName,
222                                                        bool checkingAlternateName)
223 {
224 #if PLATFORM(IOS)
225     FontLocker fontLocker;
226 #endif
227     
228 #if OS(WINDOWS) && ENABLE(OPENTYPE_VERTICAL)
229     // Leading "@" in the font name enables Windows vertical flow flag for the font.
230     // Because we do vertical flow by ourselves, we don't want to use the Windows feature.
231     // IE disregards "@" regardless of the orientatoin, so we follow the behavior.
232     const AtomicString& familyName = (passedFamilyName.isEmpty() || passedFamilyName[0] != '@') ?
233         passedFamilyName : AtomicString(passedFamilyName.impl()->substring(1));
234 #else
235     const AtomicString& familyName = passedFamilyName;
236 #endif
237
238     if (!gFontPlatformDataCache) {
239         gFontPlatformDataCache = new FontPlatformDataCache;
240         platformInit();
241     }
242
243     FontPlatformDataCacheKey key(familyName, fontDescription);
244
245     FontPlatformDataCache::AddResult result = gFontPlatformDataCache->add(key, nullptr);
246     FontPlatformDataCache::iterator it = result.iterator;
247     if (result.isNewEntry) {
248         it->value = createFontPlatformData(fontDescription, familyName);
249
250         if (!it->value && !checkingAlternateName) {
251             // We were unable to find a font.  We have a small set of fonts that we alias to other names,
252             // e.g., Arial/Helvetica, Courier/Courier New, etc.  Try looking up the font under the aliased name.
253             const AtomicString alternateName = alternateFamilyName(familyName);
254             if (!alternateName.isNull()) {
255                 FontPlatformData* fontPlatformDataForAlternateName = getCachedFontPlatformData(fontDescription, alternateName, true);
256                 // Lookup the key in the hash table again as the previous iterator may have
257                 // been invalidated by the recursive call to getCachedFontPlatformData().
258                 it = gFontPlatformDataCache->find(key);
259                 ASSERT(it != gFontPlatformDataCache->end());
260                 if (fontPlatformDataForAlternateName)
261                     it->value = adoptPtr(new FontPlatformData(*fontPlatformDataForAlternateName));
262             }
263         }
264     }
265
266     return it->value.get();
267 }
268
269 #if ENABLE(OPENTYPE_VERTICAL)
270 struct FontVerticalDataCacheKeyHash {
271     static unsigned hash(const FontCache::FontFileKey& fontFileKey)
272     {
273         return PtrHash<const FontCache::FontFileKey*>::hash(&fontFileKey);
274     }
275
276     static bool equal(const FontCache::FontFileKey& a, const FontCache::FontFileKey& b)
277     {
278         return a == b;
279     }
280
281     static const bool safeToCompareToEmptyOrDeleted = true;
282 };
283
284 struct FontVerticalDataCacheKeyTraits : WTF::GenericHashTraits<FontCache::FontFileKey> {
285     static const bool emptyValueIsZero = true;
286     static const bool needsDestruction = true;
287     static const FontCache::FontFileKey& emptyValue()
288     {
289         static NeverDestroyed<FontCache::FontFileKey> key = nullAtom;
290         return key;
291     }
292     static void constructDeletedValue(FontCache::FontFileKey& slot)
293     {
294         new (NotNull, &slot) FontCache::FontFileKey(HashTableDeletedValue);
295     }
296     static bool isDeletedValue(const FontCache::FontFileKey& value)
297     {
298         return value.isHashTableDeletedValue();
299     }
300 };
301
302 typedef HashMap<FontCache::FontFileKey, RefPtr<OpenTypeVerticalData>, FontVerticalDataCacheKeyHash, FontVerticalDataCacheKeyTraits> FontVerticalDataCache;
303
304 FontVerticalDataCache& fontVerticalDataCacheInstance()
305 {
306     static NeverDestroyed<FontVerticalDataCache> fontVerticalDataCache;
307     return fontVerticalDataCache;
308 }
309
310 PassRefPtr<OpenTypeVerticalData> FontCache::getVerticalData(const FontFileKey& key, const FontPlatformData& platformData)
311 {
312     FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance();
313     FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key);
314     if (result != fontVerticalDataCache.end())
315         return result.get()->value;
316
317     RefPtr<OpenTypeVerticalData> verticalData = OpenTypeVerticalData::create(platformData);
318     if (!verticalData->isOpenType())
319         verticalData.clear();
320     fontVerticalDataCache.set(key, verticalData);
321     return verticalData;
322 }
323 #endif
324
325 struct FontDataCacheKeyHash {
326     static unsigned hash(const FontPlatformData& platformData)
327     {
328         return platformData.hash();
329     }
330          
331     static bool equal(const FontPlatformData& a, const FontPlatformData& b)
332     {
333         return a == b;
334     }
335
336     static const bool safeToCompareToEmptyOrDeleted = true;
337 };
338
339 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
340     static const bool emptyValueIsZero = true;
341     static const bool needsDestruction = true;
342     static const FontPlatformData& emptyValue()
343     {
344         static NeverDestroyed<FontPlatformData> key(0.f, false, false);
345         return key;
346     }
347     static void constructDeletedValue(FontPlatformData& slot)
348     {
349         new (NotNull, &slot) FontPlatformData(HashTableDeletedValue);
350     }
351     static bool isDeletedValue(const FontPlatformData& value)
352     {
353         return value.isHashTableDeletedValue();
354     }
355 };
356
357 typedef HashMap<FontPlatformData, std::pair<RefPtr<SimpleFontData>, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
358
359 static FontDataCache* gFontDataCache = 0;
360
361 #if PLATFORM(IOS)
362 const int cMaxInactiveFontData = 120;
363 const int cTargetInactiveFontData = 100;
364 const int cMaxUnderMemoryPressureInactiveFontData = 50;
365 const int cTargetUnderMemoryPressureInactiveFontData = 30;
366 #else
367 const int cMaxInactiveFontData = 225;
368 const int cTargetInactiveFontData = 200;
369 #endif
370 static ListHashSet<RefPtr<SimpleFontData>>* gInactiveFontData = 0;
371
372 PassRefPtr<SimpleFontData> FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName, ShouldRetain shouldRetain)
373 {
374     FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName);
375     if (!platformData)
376         return 0;
377
378     return getCachedFontData(platformData, shouldRetain);
379 }
380
381 PassRefPtr<SimpleFontData> FontCache::getCachedFontData(const FontPlatformData* platformData, ShouldRetain shouldRetain)
382 {
383     if (!platformData)
384         return 0;
385
386 #if !ASSERT_DISABLED
387     if (shouldRetain == DoNotRetain)
388         ASSERT(m_purgePreventCount);
389 #endif
390
391 #if PLATFORM(IOS)
392     FontLocker fontLocker;
393 #endif
394     
395     if (!gFontDataCache) {
396         gFontDataCache = new FontDataCache;
397         gInactiveFontData = new ListHashSet<RefPtr<SimpleFontData>>;
398     }
399
400     FontDataCache::iterator result = gFontDataCache->find(*platformData);
401     if (result == gFontDataCache->end()) {
402         std::pair<RefPtr<SimpleFontData>, unsigned> newValue(SimpleFontData::create(*platformData), shouldRetain == Retain ? 1 : 0);
403         gFontDataCache->set(*platformData, newValue);
404         if (shouldRetain == DoNotRetain)
405             gInactiveFontData->add(newValue.first);
406         return newValue.first.release();
407     }
408
409     if (!result.get()->value.second) {
410         ASSERT(gInactiveFontData->contains(result.get()->value.first));
411         gInactiveFontData->remove(result.get()->value.first);
412     }
413
414     if (shouldRetain == Retain)
415         result.get()->value.second++;
416     else if (!result.get()->value.second) {
417         // If shouldRetain is DoNotRetain and count is 0, we want to remove the fontData from 
418         // gInactiveFontData (above) and re-add here to update LRU position.
419         gInactiveFontData->add(result.get()->value.first);
420     }
421
422     return result.get()->value.first;
423 }
424
425 SimpleFontData* FontCache::getNonRetainedLastResortFallbackFont(const FontDescription& fontDescription)
426 {
427     return getLastResortFallbackFont(fontDescription, DoNotRetain).leakRef();
428 }
429
430 void FontCache::releaseFontData(const SimpleFontData* fontData)
431 {
432     ASSERT(gFontDataCache);
433     ASSERT(!fontData->isCustomFont());
434
435 #if PLATFORM(IOS)
436     FontLocker fontLocker;
437 #endif
438     
439     FontDataCache::iterator it = gFontDataCache->find(fontData->platformData());
440     ASSERT(it != gFontDataCache->end());
441     if (it == gFontDataCache->end())
442         return;
443
444     ASSERT(it->value.second);
445     if (!--it->value.second)
446         gInactiveFontData->add(it->value.first);
447 }
448
449 void FontCache::purgeInactiveFontDataIfNeeded()
450 {
451 #if PLATFORM(IOS)
452     bool underMemoryPressure = memoryPressureHandler().hasReceivedMemoryPressure();
453     int inactiveFontDataLimit = underMemoryPressure ? cMaxUnderMemoryPressureInactiveFontData : cMaxInactiveFontData;
454     int targetFontDataLimit = underMemoryPressure ? cTargetUnderMemoryPressureInactiveFontData : cTargetInactiveFontData;
455
456     if (gInactiveFontData && !m_purgePreventCount && gInactiveFontData->size() > inactiveFontDataLimit)
457         purgeInactiveFontData(gInactiveFontData->size() - targetFontDataLimit);
458 #else
459     if (gInactiveFontData && !m_purgePreventCount && gInactiveFontData->size() > cMaxInactiveFontData)
460         purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData);
461 #endif
462 }
463
464 void FontCache::purgeInactiveFontData(int count)
465 {
466     pruneUnreferencedEntriesFromFontGlyphsCache();
467
468     if (!gInactiveFontData || m_purgePreventCount)
469         return;
470
471     static bool isPurging;  // Guard against reentry when e.g. a deleted FontData releases its small caps FontData.
472     if (isPurging)
473         return;
474
475     isPurging = true;
476
477 #if PLATFORM(IOS)
478     FontLocker fontLocker;
479 #endif
480
481     Vector<RefPtr<SimpleFontData>, 20> fontDataToDelete;
482     ListHashSet<RefPtr<SimpleFontData>>::iterator end = gInactiveFontData->end();
483     ListHashSet<RefPtr<SimpleFontData>>::iterator it = gInactiveFontData->begin();
484     for (int i = 0; i < count && it != end; ++it, ++i) {
485         RefPtr<SimpleFontData>& fontData = *it.get();
486         gFontDataCache->remove(fontData->platformData());
487         // We should not delete SimpleFontData here because deletion can modify gInactiveFontData. See http://trac.webkit.org/changeset/44011
488         fontDataToDelete.append(fontData);
489     }
490
491     if (it == end) {
492         // Removed everything
493         gInactiveFontData->clear();
494     } else {
495         for (int i = 0; i < count; ++i)
496             gInactiveFontData->remove(gInactiveFontData->begin());
497     }
498
499     fontDataToDelete.clear();
500
501     if (gFontPlatformDataCache) {
502         Vector<FontPlatformDataCacheKey> keysToRemove;
503         keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size());
504         FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end();
505         for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) {
506             if (platformData->value && !gFontDataCache->contains(*platformData->value))
507                 keysToRemove.append(platformData->key);
508         }
509         
510         size_t keysToRemoveCount = keysToRemove.size();
511         for (size_t i = 0; i < keysToRemoveCount; ++i)
512             gFontPlatformDataCache->remove(keysToRemove[i]);
513     }
514
515 #if ENABLE(OPENTYPE_VERTICAL)
516     FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance();
517     if (!fontVerticalDataCache.isEmpty()) {
518         // Mark & sweep unused verticalData
519         FontVerticalDataCache::iterator verticalDataEnd = fontVerticalDataCache.end();
520         for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) {
521             if (verticalData->value)
522                 verticalData->value->m_inFontCache = false;
523         }
524         FontDataCache::iterator fontDataEnd = gFontDataCache->end();
525         for (FontDataCache::iterator fontData = gFontDataCache->begin(); fontData != fontDataEnd; ++fontData) {
526             OpenTypeVerticalData* verticalData = const_cast<OpenTypeVerticalData*>(fontData->value.first->verticalData());
527             if (verticalData)
528                 verticalData->m_inFontCache = true;
529         }
530         Vector<FontFileKey> keysToRemove;
531         keysToRemove.reserveInitialCapacity(fontVerticalDataCache.size());
532         for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) {
533             if (!verticalData->value || !verticalData->value->m_inFontCache)
534                 keysToRemove.append(verticalData->key);
535         }
536         for (size_t i = 0, count = keysToRemove.size(); i < count; ++i)
537             fontVerticalDataCache.take(keysToRemove[i]);
538     }
539 #endif
540
541     isPurging = false;
542 }
543
544 size_t FontCache::fontDataCount()
545 {
546     if (gFontDataCache)
547         return gFontDataCache->size();
548     return 0;
549 }
550
551 size_t FontCache::inactiveFontDataCount()
552 {
553     if (gInactiveFontData)
554         return gInactiveFontData->size();
555     return 0;
556 }
557
558 PassRefPtr<FontData> FontCache::getFontData(const FontDescription& description, int& familyIndex, FontSelector* fontSelector)
559 {
560     ASSERT(familyIndex != cAllFamiliesScanned);
561     RefPtr<FontData> result;
562
563     bool isFirst = !familyIndex;
564     int familyCount = description.familyCount();
565     for (;familyIndex < familyCount && !result; ++familyIndex) {
566         const AtomicString& family = description.familyAt(familyIndex);
567         if (family.isEmpty())
568             continue;
569         if (fontSelector)
570             result = fontSelector->getFontData(description, family);
571         if (!result)
572             result = getCachedFontData(description, family);
573     }
574     if (familyIndex == familyCount)
575         familyIndex = cAllFamiliesScanned;
576
577 #if PLATFORM(COCOA)
578     if (!result) {
579         // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
580         // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
581         // Geeza Pro font.
582         result = similarFontPlatformData(description);
583     }
584 #endif
585
586     if (!result && isFirst) {
587         // If it's the primary font that we couldn't find, we try the following. In all other cases, we will
588         // just use per-character system fallback.
589         if (fontSelector) {
590             // Try the user's preferred standard font.
591             if (RefPtr<FontData> data = fontSelector->getFontData(description, standardFamily))
592                 return data.release();
593         }
594         // Still no result.  Hand back our last resort fallback font.
595         result = getLastResortFallbackFont(description);
596     }
597     return result.release();
598 }
599
600 static HashSet<FontSelector*>* gClients;
601
602 void FontCache::addClient(FontSelector* client)
603 {
604     if (!gClients)
605         gClients = new HashSet<FontSelector*>;
606
607     ASSERT(!gClients->contains(client));
608     gClients->add(client);
609 }
610
611 void FontCache::removeClient(FontSelector* client)
612 {
613     ASSERT(gClients);
614     ASSERT(gClients->contains(client));
615
616     gClients->remove(client);
617 }
618
619 static unsigned short gGeneration = 0;
620
621 unsigned short FontCache::generation()
622 {
623     return gGeneration;
624 }
625
626 void FontCache::invalidate()
627 {
628     if (!gClients) {
629         ASSERT(!gFontPlatformDataCache);
630         return;
631     }
632
633     if (gFontPlatformDataCache)
634         gFontPlatformDataCache->clear();
635     invalidateFontGlyphsCache();
636
637     gGeneration++;
638
639     Vector<Ref<FontSelector>> clients;
640     clients.reserveInitialCapacity(gClients->size());
641     for (auto it = gClients->begin(), end = gClients->end(); it != end; ++it)
642         clients.uncheckedAppend(**it);
643
644     for (unsigned i = 0; i < clients.size(); ++i)
645         clients[i]->fontCacheInvalidated();
646
647     purgeInactiveFontData();
648 }
649
650 } // namespace WebCore