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