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