Access FontCache global instance via singleton() static member function
[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 "FontCascade.h"
34 #include "FontGlyphs.h"
35 #include "FontPlatformData.h"
36 #include "FontSelector.h"
37 #include "MemoryPressureHandler.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/TemporaryChange.h>
44 #include <wtf/text/AtomicStringHash.h>
45 #include <wtf/text/StringHash.h>
46
47 #if ENABLE(OPENTYPE_VERTICAL)
48 #include "OpenTypeVerticalData.h"
49 #endif
50
51 #if PLATFORM(IOS)
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 fontLock;
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(&fontLock, &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(&fontLock);
75         ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontLock lock failed with code:%d", lockcode);    
76     }
77     ~FontLocker()
78     {
79         int lockcode = pthread_mutex_unlock(&fontLock);
80         ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontLock unlock failed with code:%d", lockcode);
81     }
82 };
83 #endif // PLATFORM(IOS)
84
85 using namespace WTF;
86
87 namespace WebCore {
88
89 FontCache& FontCache::singleton()
90 {
91     static NeverDestroyed<FontCache> globalFontCache;
92     return globalFontCache;
93 }
94
95 FontCache::FontCache()
96     : m_purgeTimer(*this, &FontCache::purgeTimerFired)
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, std::unique_ptr<FontPlatformData>, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
146
147 static FontPlatformDataCache& fontPlatformDataCache()
148 {
149     static NeverDestroyed<FontPlatformDataCache> cache;
150     return cache;
151 }
152
153 static bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char* reference, unsigned length)
154 {
155     ASSERT(length > 0);
156     ASSERT(familyName.length() == length);
157     ASSERT(strlen(reference) == length);
158     const AtomicStringImpl* familyNameImpl = familyName.impl();
159     if (familyNameImpl->is8Bit())
160         return equalIgnoringCase(familyNameImpl->characters8(), reinterpret_cast<const LChar*>(reference), length);
161     return equalIgnoringCase(familyNameImpl->characters16(), reinterpret_cast<const LChar*>(reference), length);
162 }
163
164 template<size_t length>
165 static inline bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char (&reference)[length])
166 {
167     return familyNameEqualIgnoringCase(familyName, reference, length - 1);
168 }
169
170 static const AtomicString alternateFamilyName(const AtomicString& familyName)
171 {
172     // Alias Courier and Courier New.
173     // Alias Times and Times New Roman.
174     // Alias Arial and Helvetica.
175     switch (familyName.length()) {
176     case 5:
177         if (familyNameEqualIgnoringCase(familyName, "Arial"))
178             return AtomicString("Helvetica", AtomicString::ConstructFromLiteral);
179         if (familyNameEqualIgnoringCase(familyName, "Times"))
180             return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
181         break;
182     case 7:
183         if (familyNameEqualIgnoringCase(familyName, "Courier"))
184             return AtomicString("Courier New", AtomicString::ConstructFromLiteral);
185         break;
186     case 9:
187         if (familyNameEqualIgnoringCase(familyName, "Helvetica"))
188             return AtomicString("Arial", AtomicString::ConstructFromLiteral);
189         break;
190 #if !OS(WINDOWS)
191     // On Windows, Courier New (truetype font) is always present and
192     // Courier is a bitmap font. So, we don't want to map Courier New to
193     // Courier.
194     case 11:
195         if (familyNameEqualIgnoringCase(familyName, "Courier New"))
196             return AtomicString("Courier", AtomicString::ConstructFromLiteral);
197         break;
198 #endif // !OS(WINDOWS)
199     case 15:
200         if (familyNameEqualIgnoringCase(familyName, "Times New Roman"))
201             return AtomicString("Times", AtomicString::ConstructFromLiteral);
202         break;
203 #if OS(WINDOWS)
204     // On Windows, bitmap fonts are blocked altogether so that we have to 
205     // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font)
206     case 13:
207         if (familyNameEqualIgnoringCase(familyName, "MS Sans Serif"))
208             return AtomicString("Microsoft Sans Serif", AtomicString::ConstructFromLiteral);
209         break;
210
211     // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no 
212     // 'Microsoft Sans Serif-equivalent' for Serif.
213     case 8:
214         if (familyNameEqualIgnoringCase(familyName, "MS Serif"))
215             return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
216         break;
217 #endif // OS(WINDOWS)
218
219     }
220
221     return nullAtom;
222 }
223
224 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription,
225                                                        const AtomicString& passedFamilyName,
226                                                        bool checkingAlternateName)
227 {
228 #if PLATFORM(IOS)
229     FontLocker fontLocker;
230 #endif
231     
232 #if OS(WINDOWS) && ENABLE(OPENTYPE_VERTICAL)
233     // Leading "@" in the font name enables Windows vertical flow flag for the font.
234     // Because we do vertical flow by ourselves, we don't want to use the Windows feature.
235     // IE disregards "@" regardless of the orientatoin, so we follow the behavior.
236     const AtomicString& familyName = (passedFamilyName.isEmpty() || passedFamilyName[0] != '@') ?
237         passedFamilyName : AtomicString(passedFamilyName.impl()->substring(1));
238 #else
239     const AtomicString& familyName = passedFamilyName;
240 #endif
241
242     static bool initialized;
243     if (!initialized) {
244         platformInit();
245         initialized = true;
246     }
247
248     FontPlatformDataCacheKey key(familyName, fontDescription);
249
250     auto addResult = fontPlatformDataCache().add(key, nullptr);
251     FontPlatformDataCache::iterator it = addResult.iterator;
252     if (addResult.isNewEntry) {
253         it->value = createFontPlatformData(fontDescription, familyName);
254
255         if (!it->value && !checkingAlternateName) {
256             // We were unable to find a font.  We have a small set of fonts that we alias to other names,
257             // e.g., Arial/Helvetica, Courier/Courier New, etc.  Try looking up the font under the aliased name.
258             const AtomicString alternateName = alternateFamilyName(familyName);
259             if (!alternateName.isNull()) {
260                 FontPlatformData* fontPlatformDataForAlternateName = getCachedFontPlatformData(fontDescription, alternateName, true);
261                 // Lookup the key in the hash table again as the previous iterator may have
262                 // been invalidated by the recursive call to getCachedFontPlatformData().
263                 it = fontPlatformDataCache().find(key);
264                 ASSERT(it != fontPlatformDataCache().end());
265                 if (fontPlatformDataForAlternateName)
266                     it->value = std::make_unique<FontPlatformData>(*fontPlatformDataForAlternateName);
267             }
268         }
269     }
270
271     return it->value.get();
272 }
273
274 #if ENABLE(OPENTYPE_VERTICAL)
275 struct FontVerticalDataCacheKeyHash {
276     static unsigned hash(const FontCache::FontFileKey& fontFileKey)
277     {
278         return PtrHash<const FontCache::FontFileKey*>::hash(&fontFileKey);
279     }
280
281     static bool equal(const FontCache::FontFileKey& a, const FontCache::FontFileKey& b)
282     {
283         return a == b;
284     }
285
286     static const bool safeToCompareToEmptyOrDeleted = true;
287 };
288
289 struct FontVerticalDataCacheKeyTraits : WTF::GenericHashTraits<FontCache::FontFileKey> {
290     static const bool emptyValueIsZero = true;
291     static const bool needsDestruction = true;
292     static const FontCache::FontFileKey& emptyValue()
293     {
294         static NeverDestroyed<FontCache::FontFileKey> key = nullAtom;
295         return key;
296     }
297     static void constructDeletedValue(FontCache::FontFileKey& slot)
298     {
299         new (NotNull, &slot) FontCache::FontFileKey(HashTableDeletedValue);
300     }
301     static bool isDeletedValue(const FontCache::FontFileKey& value)
302     {
303         return value.isHashTableDeletedValue();
304     }
305 };
306
307 typedef HashMap<FontCache::FontFileKey, RefPtr<OpenTypeVerticalData>, FontVerticalDataCacheKeyHash, FontVerticalDataCacheKeyTraits> FontVerticalDataCache;
308
309 FontVerticalDataCache& fontVerticalDataCacheInstance()
310 {
311     static NeverDestroyed<FontVerticalDataCache> fontVerticalDataCache;
312     return fontVerticalDataCache;
313 }
314
315 PassRefPtr<OpenTypeVerticalData> FontCache::getVerticalData(const FontFileKey& key, const FontPlatformData& platformData)
316 {
317     FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance();
318     FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key);
319     if (result != fontVerticalDataCache.end())
320         return result.get()->value;
321
322     RefPtr<OpenTypeVerticalData> verticalData = OpenTypeVerticalData::create(platformData);
323     if (!verticalData->isOpenType())
324         verticalData.clear();
325     fontVerticalDataCache.set(key, verticalData);
326     return verticalData;
327 }
328 #endif
329
330 struct FontDataCacheKeyHash {
331     static unsigned hash(const FontPlatformData& platformData)
332     {
333         return platformData.hash();
334     }
335          
336     static bool equal(const FontPlatformData& a, const FontPlatformData& b)
337     {
338         return a == b;
339     }
340
341     static const bool safeToCompareToEmptyOrDeleted = true;
342 };
343
344 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
345     static const bool emptyValueIsZero = true;
346     static const bool needsDestruction = true;
347     static const FontPlatformData& emptyValue()
348     {
349         static NeverDestroyed<FontPlatformData> key(0.f, false, false);
350         return key;
351     }
352     static void constructDeletedValue(FontPlatformData& slot)
353     {
354         new (NotNull, &slot) FontPlatformData(HashTableDeletedValue);
355     }
356     static bool isDeletedValue(const FontPlatformData& value)
357     {
358         return value.isHashTableDeletedValue();
359     }
360 };
361
362 typedef HashMap<FontPlatformData, RefPtr<Font>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
363
364 static FontDataCache& cachedFonts()
365 {
366     static NeverDestroyed<FontDataCache> cache;
367     return cache;
368 }
369
370
371 #if PLATFORM(IOS)
372 const int cMaxInactiveFontData = 120;
373 const int cTargetInactiveFontData = 100;
374 #else
375 const int cMaxInactiveFontData = 225;
376 const int cTargetInactiveFontData = 200;
377 #endif
378
379 const int cMaxUnderMemoryPressureInactiveFontData = 50;
380 const int cTargetUnderMemoryPressureInactiveFontData = 30;
381
382 RefPtr<Font> FontCache::fontForFamily(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName)
383 {
384     if (!m_purgeTimer.isActive())
385         m_purgeTimer.startOneShot(std::chrono::milliseconds::zero());
386
387     FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName);
388     if (!platformData)
389         return nullptr;
390
391     return fontForPlatformData(*platformData);
392 }
393
394 Ref<Font> FontCache::fontForPlatformData(const FontPlatformData& platformData)
395 {
396 #if PLATFORM(IOS)
397     FontLocker fontLocker;
398 #endif
399     
400     auto addResult = cachedFonts().add(platformData, nullptr);
401     if (addResult.isNewEntry)
402         addResult.iterator->value = Font::create(platformData);
403
404     return *addResult.iterator->value;
405 }
406
407 void FontCache::purgeTimerFired()
408 {
409     purgeInactiveFontDataIfNeeded();
410 }
411
412 void FontCache::purgeInactiveFontDataIfNeeded()
413 {
414     bool underMemoryPressure = MemoryPressureHandler::singleton().isUnderMemoryPressure();
415     int inactiveFontDataLimit = underMemoryPressure ? cMaxUnderMemoryPressureInactiveFontData : cMaxInactiveFontData;
416
417     if (cachedFonts().size() < inactiveFontDataLimit)
418         return;
419     int inactiveCount = inactiveFontCount();
420     if (inactiveCount <= inactiveFontDataLimit)
421         return;
422
423     int targetFontDataLimit = underMemoryPressure ? cTargetUnderMemoryPressureInactiveFontData : cTargetInactiveFontData;
424     purgeInactiveFontData(inactiveCount - targetFontDataLimit);
425 }
426
427 void FontCache::purgeInactiveFontData(int purgeCount)
428 {
429     pruneUnreferencedEntriesFromFontGlyphsCache();
430     pruneSystemFallbackFonts();
431
432 #if PLATFORM(IOS)
433     FontLocker fontLocker;
434 #endif
435
436     while (purgeCount) {
437         Vector<RefPtr<Font>, 20> fontsToDelete;
438         for (auto& font : cachedFonts().values()) {
439             if (!font->hasOneRef())
440                 continue;
441             fontsToDelete.append(WTF::move(font));
442             if (!--purgeCount)
443                 break;
444         }
445         // Fonts may ref other fonts so we loop until there are no changes.
446         if (fontsToDelete.isEmpty())
447             break;
448         for (auto& font : fontsToDelete)
449             cachedFonts().remove(font->platformData());
450     };
451
452     Vector<FontPlatformDataCacheKey> keysToRemove;
453     keysToRemove.reserveInitialCapacity(fontPlatformDataCache().size());
454     for (auto& entry : fontPlatformDataCache()) {
455         if (entry.value && !cachedFonts().contains(*entry.value))
456             keysToRemove.append(entry.key);
457     }
458     for (auto key : keysToRemove)
459         fontPlatformDataCache().remove(key);
460
461 #if ENABLE(OPENTYPE_VERTICAL)
462     FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance();
463     if (!fontVerticalDataCache.isEmpty()) {
464         // Mark & sweep unused verticalData
465         for (auto& verticalData : fontVerticalDataCache.values()) {
466             if (verticalData)
467                 verticalData->m_inFontCache = false;
468         }
469         for (auto& font : cachedFonts().values()) {
470             auto* verticalData = const_cast<OpenTypeVerticalData*>(font->verticalData());
471             if (verticalData)
472                 verticalData->m_inFontCache = true;
473         }
474         Vector<FontFileKey> keysToRemove;
475         keysToRemove.reserveInitialCapacity(fontVerticalDataCache.size());
476         for (auto& it : fontVerticalDataCache) {
477             if (!it.value || !it.value->m_inFontCache)
478                 keysToRemove.append(it.key);
479         }
480         for (auto& key : keysToRemove)
481             fontVerticalDataCache.remove(key);
482     }
483 #endif
484 }
485
486 size_t FontCache::fontCount()
487 {
488     return cachedFonts().size();
489 }
490
491 size_t FontCache::inactiveFontCount()
492 {
493 #if PLATFORM(IOS)
494     FontLocker fontLocker;
495 #endif
496     unsigned count = 0;
497     for (auto& font : cachedFonts().values()) {
498         if (font->hasOneRef())
499             ++count;
500     }
501     return count;
502 }
503
504 static HashSet<FontSelector*>* gClients;
505
506 void FontCache::addClient(FontSelector* client)
507 {
508     if (!gClients)
509         gClients = new HashSet<FontSelector*>;
510
511     ASSERT(!gClients->contains(client));
512     gClients->add(client);
513 }
514
515 void FontCache::removeClient(FontSelector* client)
516 {
517     ASSERT(gClients);
518     ASSERT(gClients->contains(client));
519
520     gClients->remove(client);
521 }
522
523 static unsigned short gGeneration = 0;
524
525 unsigned short FontCache::generation()
526 {
527     return gGeneration;
528 }
529
530 void FontCache::invalidate()
531 {
532     if (!gClients) {
533         ASSERT(fontPlatformDataCache().isEmpty());
534         return;
535     }
536
537     fontPlatformDataCache().clear();
538     invalidateFontGlyphsCache();
539
540     gGeneration++;
541
542     Vector<Ref<FontSelector>> clients;
543     clients.reserveInitialCapacity(gClients->size());
544     for (auto it = gClients->begin(), end = gClients->end(); it != end; ++it)
545         clients.uncheckedAppend(**it);
546
547     for (unsigned i = 0; i < clients.size(); ++i)
548         clients[i]->fontCacheInvalidated();
549
550     purgeInactiveFontData();
551 }
552
553 #if !PLATFORM(COCOA)
554 RefPtr<Font> FontCache::similarFont(const FontDescription&)
555 {
556     return nullptr;
557 }
558 #endif
559
560 } // namespace WebCore