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