[WTF] Clean up StringStatics.cpp by using LazyNeverDestroyed<> for Atoms
[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 "WebKitFontFamilyNames.h"
37 #include <wtf/HashMap.h>
38 #include <wtf/MemoryPressureHandler.h>
39 #include <wtf/NeverDestroyed.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 USE(DIRECT2D)
49 #include <dwrite.h>
50 #endif
51
52 #if PLATFORM(IOS)
53 #include <wtf/Noncopyable.h>
54
55 // FIXME: We may be able to simplify this code using C++11 threading primitives, including std::call_once().
56 static pthread_mutex_t fontLock;
57
58 static void initFontCacheLockOnce()
59 {
60     pthread_mutexattr_t mutexAttribute;
61     pthread_mutexattr_init(&mutexAttribute);
62     pthread_mutexattr_settype(&mutexAttribute, PTHREAD_MUTEX_RECURSIVE);
63     pthread_mutex_init(&fontLock, &mutexAttribute);
64     pthread_mutexattr_destroy(&mutexAttribute);
65 }
66
67 static pthread_once_t initFontLockControl = PTHREAD_ONCE_INIT;
68
69 class FontLocker {
70     WTF_MAKE_NONCOPYABLE(FontLocker);
71 public:
72     FontLocker()
73     {
74         pthread_once(&initFontLockControl, initFontCacheLockOnce);
75         int lockcode = pthread_mutex_lock(&fontLock);
76         ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontLock lock failed with code:%d", lockcode);    
77     }
78     ~FontLocker()
79     {
80         int lockcode = pthread_mutex_unlock(&fontLock);
81         ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontLock unlock failed with code:%d", lockcode);
82     }
83 };
84 #endif // PLATFORM(IOS)
85
86 using namespace WTF;
87
88 namespace WebCore {
89
90 FontCache& FontCache::singleton()
91 {
92     static NeverDestroyed<FontCache> globalFontCache;
93     return globalFontCache;
94 }
95
96 FontCache::FontCache()
97     : m_purgeTimer(*this, &FontCache::purgeInactiveFontDataIfNeeded)
98 {
99 }
100
101 struct FontPlatformDataCacheKey {
102     WTF_MAKE_FAST_ALLOCATED;
103 public:
104     FontPlatformDataCacheKey() { }
105     FontPlatformDataCacheKey(const AtomicString& family, const FontDescription& description, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, FontSelectionSpecifiedCapabilities fontFaceCapabilities)
106         : m_fontDescriptionKey(description)
107         , m_family(family)
108         , m_fontFaceFeatures(fontFaceFeatures ? *fontFaceFeatures : FontFeatureSettings())
109         , m_fontFaceVariantSettings(fontFaceVariantSettings ? *fontFaceVariantSettings : FontVariantSettings())
110         , m_fontFaceCapabilities(fontFaceCapabilities)
111     { }
112
113     explicit FontPlatformDataCacheKey(HashTableDeletedValueType t)
114         : m_fontDescriptionKey(t)
115     { }
116
117     bool isHashTableDeletedValue() const { return m_fontDescriptionKey.isHashTableDeletedValue(); }
118
119     bool operator==(const FontPlatformDataCacheKey& other) const
120     {
121         if (m_fontDescriptionKey != other.m_fontDescriptionKey
122             || m_fontFaceFeatures != other.m_fontFaceFeatures
123             || m_fontFaceVariantSettings != other.m_fontFaceVariantSettings
124             || m_fontFaceCapabilities != other.m_fontFaceCapabilities)
125             return false;
126         if (m_family.impl() == other.m_family.impl())
127             return true;
128         if (m_family.isNull() || other.m_family.isNull())
129             return false;
130         return ASCIICaseInsensitiveHash::equal(m_family, other.m_family);
131     }
132
133     FontDescriptionKey m_fontDescriptionKey;
134     AtomicString m_family;
135     FontFeatureSettings m_fontFaceFeatures;
136     FontVariantSettings m_fontFaceVariantSettings;
137     FontSelectionSpecifiedCapabilities m_fontFaceCapabilities;
138 };
139
140 struct FontPlatformDataCacheKeyHash {
141     static unsigned hash(const FontPlatformDataCacheKey& fontKey)
142     {
143         IntegerHasher hasher;
144         hasher.add(ASCIICaseInsensitiveHash::hash(fontKey.m_family));
145         hasher.add(fontKey.m_fontDescriptionKey.computeHash());
146         hasher.add(fontKey.m_fontFaceFeatures.hash());
147         hasher.add(fontKey.m_fontFaceVariantSettings.uniqueValue());
148         if (auto weight = fontKey.m_fontFaceCapabilities.weight)
149             hasher.add(weight->uniqueValue());
150         else
151             hasher.add(std::numeric_limits<unsigned>::max());
152         if (auto width = fontKey.m_fontFaceCapabilities.weight)
153             hasher.add(width->uniqueValue());
154         else
155             hasher.add(std::numeric_limits<unsigned>::max());
156         if (auto slope = fontKey.m_fontFaceCapabilities.weight)
157             hasher.add(slope->uniqueValue());
158         else
159             hasher.add(std::numeric_limits<unsigned>::max());
160         return hasher.hash();
161     }
162          
163     static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
164     {
165         return a == b;
166     }
167
168     static const bool safeToCompareToEmptyOrDeleted = true;
169 };
170
171 struct FontPlatformDataCacheKeyHashTraits : public SimpleClassHashTraits<FontPlatformDataCacheKey> {
172     static const bool emptyValueIsZero = false;
173 };
174
175 typedef HashMap<FontPlatformDataCacheKey, std::unique_ptr<FontPlatformData>, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyHashTraits> FontPlatformDataCache;
176
177 static FontPlatformDataCache& fontPlatformDataCache()
178 {
179     static NeverDestroyed<FontPlatformDataCache> cache;
180     return cache;
181 }
182
183 const AtomicString& FontCache::alternateFamilyName(const AtomicString& familyName)
184 {
185     static NeverDestroyed<AtomicString> arial("Arial", AtomicString::ConstructFromLiteral);
186     static NeverDestroyed<AtomicString> courier("Courier", AtomicString::ConstructFromLiteral);
187     static NeverDestroyed<AtomicString> courierNew("Courier New", AtomicString::ConstructFromLiteral);
188     static NeverDestroyed<AtomicString> helvetica("Helvetica", AtomicString::ConstructFromLiteral);
189     static NeverDestroyed<AtomicString> times("Times", AtomicString::ConstructFromLiteral);
190     static NeverDestroyed<AtomicString> timesNewRoman("Times New Roman", AtomicString::ConstructFromLiteral);
191
192     const AtomicString& platformSpecificAlternate = platformAlternateFamilyName(familyName);
193     if (!platformSpecificAlternate.isNull())
194         return platformSpecificAlternate;
195
196     switch (familyName.length()) {
197     case 5:
198         if (equalLettersIgnoringASCIICase(familyName, "arial"))
199             return helvetica;
200         if (equalLettersIgnoringASCIICase(familyName, "times"))
201             return timesNewRoman;
202         break;
203     case 7:
204         if (equalLettersIgnoringASCIICase(familyName, "courier"))
205             return courierNew;
206         break;
207     case 9:
208         if (equalLettersIgnoringASCIICase(familyName, "helvetica"))
209             return arial;
210         break;
211 #if !OS(WINDOWS)
212     // On Windows, Courier New is a TrueType font that is always present and
213     // Courier is a bitmap font that we do not support. So, we don't want to map
214     // Courier New to Courier.
215     // FIXME: Not sure why this is harmful on Windows, since the alternative will
216     // only be tried if Courier New is not found.
217     case 11:
218         if (equalLettersIgnoringASCIICase(familyName, "courier new"))
219             return courier;
220         break;
221 #endif
222     case 15:
223         if (equalLettersIgnoringASCIICase(familyName, "times new roman"))
224             return times;
225         break;
226     }
227
228     return nullAtom();
229 }
230
231 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription, const AtomicString& passedFamilyName,
232     const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, FontSelectionSpecifiedCapabilities fontFaceCapabilities, bool checkingAlternateName)
233 {
234 #if PLATFORM(IOS)
235     FontLocker fontLocker;
236 #endif
237     
238 #if OS(WINDOWS) && ENABLE(OPENTYPE_VERTICAL)
239     // Leading "@" in the font name enables Windows vertical flow flag for the font.
240     // Because we do vertical flow by ourselves, we don't want to use the Windows feature.
241     // IE disregards "@" regardless of the orientatoin, so we follow the behavior.
242     const AtomicString& familyName = (passedFamilyName.isEmpty() || passedFamilyName[0] != '@') ?
243         passedFamilyName : AtomicString(passedFamilyName.impl()->substring(1));
244 #else
245     const AtomicString& familyName = passedFamilyName;
246 #endif
247
248     static bool initialized;
249     if (!initialized) {
250         platformInit();
251         initialized = true;
252     }
253
254     FontPlatformDataCacheKey key(familyName, fontDescription, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities);
255
256     auto addResult = fontPlatformDataCache().add(key, nullptr);
257     FontPlatformDataCache::iterator it = addResult.iterator;
258     if (addResult.isNewEntry) {
259         it->value = createFontPlatformData(fontDescription, familyName, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities);
260
261         if (!it->value && !checkingAlternateName) {
262             // We were unable to find a font.  We have a small set of fonts that we alias to other names,
263             // e.g., Arial/Helvetica, Courier/Courier New, etc.  Try looking up the font under the aliased name.
264             const AtomicString& alternateName = alternateFamilyName(familyName);
265             if (!alternateName.isNull()) {
266                 FontPlatformData* fontPlatformDataForAlternateName = getCachedFontPlatformData(fontDescription, alternateName, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities, true);
267                 // Lookup the key in the hash table again as the previous iterator may have
268                 // been invalidated by the recursive call to getCachedFontPlatformData().
269                 it = fontPlatformDataCache().find(key);
270                 ASSERT(it != fontPlatformDataCache().end());
271                 if (fontPlatformDataForAlternateName)
272                     it->value = std::make_unique<FontPlatformData>(*fontPlatformDataForAlternateName);
273             }
274         }
275     }
276
277     return it->value.get();
278 }
279
280 struct FontDataCacheKeyHash {
281     static unsigned hash(const FontPlatformData& platformData)
282     {
283         return platformData.hash();
284     }
285          
286     static bool equal(const FontPlatformData& a, const FontPlatformData& b)
287     {
288         return a == b;
289     }
290
291     static const bool safeToCompareToEmptyOrDeleted = true;
292 };
293
294 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
295     static const bool emptyValueIsZero = true;
296     static const FontPlatformData& emptyValue()
297     {
298         static NeverDestroyed<FontPlatformData> key(0.f, false, false);
299         return key;
300     }
301     static void constructDeletedValue(FontPlatformData& slot)
302     {
303         new (NotNull, &slot) FontPlatformData(HashTableDeletedValue);
304     }
305     static bool isDeletedValue(const FontPlatformData& value)
306     {
307         return value.isHashTableDeletedValue();
308     }
309 };
310
311 typedef HashMap<FontPlatformData, RefPtr<Font>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
312
313 static FontDataCache& cachedFonts()
314 {
315     static NeverDestroyed<FontDataCache> cache;
316     return cache;
317 }
318
319 #if ENABLE(OPENTYPE_VERTICAL)
320 typedef HashMap<FontPlatformData, RefPtr<OpenTypeVerticalData>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontVerticalDataCache;
321
322 FontVerticalDataCache& fontVerticalDataCache()
323 {
324     static NeverDestroyed<FontVerticalDataCache> fontVerticalDataCache;
325     return fontVerticalDataCache;
326 }
327
328 RefPtr<OpenTypeVerticalData> FontCache::verticalData(const FontPlatformData& platformData)
329 {
330     auto addResult = fontVerticalDataCache().ensure(platformData, [&platformData] {
331         return OpenTypeVerticalData::create(platformData);
332     });
333     return addResult.iterator->value;
334 }
335 #endif
336
337 #if PLATFORM(IOS)
338 const unsigned cMaxInactiveFontData = 120;
339 const unsigned cTargetInactiveFontData = 100;
340 #else
341 const unsigned cMaxInactiveFontData = 225;
342 const unsigned cTargetInactiveFontData = 200;
343 #endif
344
345 const unsigned cMaxUnderMemoryPressureInactiveFontData = 50;
346 const unsigned cTargetUnderMemoryPressureInactiveFontData = 30;
347
348 RefPtr<Font> FontCache::fontForFamily(const FontDescription& fontDescription, const AtomicString& family, const FontFeatureSettings* fontFaceFeatures, const FontVariantSettings* fontFaceVariantSettings, FontSelectionSpecifiedCapabilities fontFaceCapabilities, bool checkingAlternateName)
349 {
350     if (!m_purgeTimer.isActive())
351         m_purgeTimer.startOneShot(0_s);
352
353     FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, fontFaceFeatures, fontFaceVariantSettings, fontFaceCapabilities, checkingAlternateName);
354     if (!platformData)
355         return nullptr;
356
357     return fontForPlatformData(*platformData);
358 }
359
360 Ref<Font> FontCache::fontForPlatformData(const FontPlatformData& platformData)
361 {
362 #if PLATFORM(IOS)
363     FontLocker fontLocker;
364 #endif
365     
366     auto addResult = cachedFonts().add(platformData, nullptr);
367     if (addResult.isNewEntry)
368         addResult.iterator->value = Font::create(platformData);
369
370     ASSERT(addResult.iterator->value->platformData() == platformData);
371
372     return *addResult.iterator->value;
373 }
374
375 void FontCache::purgeInactiveFontDataIfNeeded()
376 {
377     bool underMemoryPressure = MemoryPressureHandler::singleton().isUnderMemoryPressure();
378     unsigned inactiveFontDataLimit = underMemoryPressure ? cMaxUnderMemoryPressureInactiveFontData : cMaxInactiveFontData;
379
380     if (cachedFonts().size() < inactiveFontDataLimit)
381         return;
382     unsigned inactiveCount = inactiveFontCount();
383     if (inactiveCount <= inactiveFontDataLimit)
384         return;
385
386     unsigned targetFontDataLimit = underMemoryPressure ? cTargetUnderMemoryPressureInactiveFontData : cTargetInactiveFontData;
387     purgeInactiveFontData(inactiveCount - targetFontDataLimit);
388 }
389
390 void FontCache::purgeInactiveFontData(unsigned purgeCount)
391 {
392     pruneUnreferencedEntriesFromFontCascadeCache();
393     pruneSystemFallbackFonts();
394
395 #if PLATFORM(IOS)
396     FontLocker fontLocker;
397 #endif
398
399     while (purgeCount) {
400         Vector<RefPtr<Font>, 20> fontsToDelete;
401         for (auto& font : cachedFonts().values()) {
402             if (!font->hasOneRef())
403                 continue;
404             fontsToDelete.append(WTFMove(font));
405             if (!--purgeCount)
406                 break;
407         }
408         // Fonts may ref other fonts so we loop until there are no changes.
409         if (fontsToDelete.isEmpty())
410             break;
411         for (auto& font : fontsToDelete) {
412             bool success = cachedFonts().remove(font->platformData());
413             ASSERT_UNUSED(success, success);
414 #if ENABLE(OPENTYPE_VERTICAL)
415             fontVerticalDataCache().remove(font->platformData());
416 #endif
417         }
418     };
419
420     Vector<FontPlatformDataCacheKey> keysToRemove;
421     keysToRemove.reserveInitialCapacity(fontPlatformDataCache().size());
422     for (auto& entry : fontPlatformDataCache()) {
423         if (entry.value && !cachedFonts().contains(*entry.value))
424             keysToRemove.uncheckedAppend(entry.key);
425     }
426     for (auto& key : keysToRemove)
427         fontPlatformDataCache().remove(key);
428
429     platformPurgeInactiveFontData();
430 }
431
432 size_t FontCache::fontCount()
433 {
434     return cachedFonts().size();
435 }
436
437 size_t FontCache::inactiveFontCount()
438 {
439 #if PLATFORM(IOS)
440     FontLocker fontLocker;
441 #endif
442     unsigned count = 0;
443     for (auto& font : cachedFonts().values()) {
444         if (font->hasOneRef())
445             ++count;
446     }
447     return count;
448 }
449
450 static HashSet<FontSelector*>* gClients;
451
452 void FontCache::addClient(FontSelector& client)
453 {
454     if (!gClients)
455         gClients = new HashSet<FontSelector*>;
456
457     ASSERT(!gClients->contains(&client));
458     gClients->add(&client);
459 }
460
461 void FontCache::removeClient(FontSelector& client)
462 {
463     ASSERT(gClients);
464     ASSERT(gClients->contains(&client));
465
466     gClients->remove(&client);
467 }
468
469 static unsigned short gGeneration = 0;
470
471 unsigned short FontCache::generation()
472 {
473     return gGeneration;
474 }
475
476 void FontCache::invalidate()
477 {
478     if (!gClients) {
479         ASSERT(fontPlatformDataCache().isEmpty());
480         return;
481     }
482
483     fontPlatformDataCache().clear();
484 #if ENABLE(OPENTYPE_VERTICAL)
485     fontVerticalDataCache().clear();
486 #endif
487     invalidateFontCascadeCache();
488
489     gGeneration++;
490
491     Vector<Ref<FontSelector>> clients;
492     clients.reserveInitialCapacity(gClients->size());
493     for (auto it = gClients->begin(), end = gClients->end(); it != end; ++it)
494         clients.uncheckedAppend(**it);
495
496     for (unsigned i = 0; i < clients.size(); ++i)
497         clients[i]->fontCacheInvalidated();
498
499     purgeInactiveFontData();
500 }
501
502 #if !PLATFORM(COCOA)
503 RefPtr<Font> FontCache::similarFont(const FontDescription&, const AtomicString&)
504 {
505     return nullptr;
506 }
507 #endif
508
509 } // namespace WebCore