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