5b508becc23fbeeb04375033b317ee6e06f2bde9
[WebKit-https.git] / Source / WebCore / platform / graphics / FontCache.cpp
1 /*
2  * Copyright (C) 2006, 2008 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 Computer, 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 "FontFallbackList.h"
35 #include "FontPlatformData.h"
36 #include "FontSelector.h"
37 #include <wtf/HashMap.h>
38 #include <wtf/ListHashSet.h>
39 #include <wtf/StdLibExtras.h>
40 #include <wtf/text/StringHash.h>
41
42 using namespace WTF;
43
44 namespace WebCore {
45
46 FontCache* fontCache()
47 {
48     DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ());
49     return &globalFontCache;
50 }
51
52 FontCache::FontCache()
53 {
54 }
55
56 struct FontPlatformDataCacheKey {
57     WTF_MAKE_FAST_ALLOCATED;
58 public:
59     FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false,
60                              bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode, FontOrientation orientation = Horizontal, FontWidthVariant widthVariant = RegularWidth)
61         : m_size(size)
62         , m_weight(weight)
63         , m_family(family)
64         , m_italic(italic)
65         , m_printerFont(isPrinterFont)
66         , m_renderingMode(renderingMode)
67         , m_orientation(orientation)
68         , m_widthVariant(widthVariant)
69     {
70     }
71
72     FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { }
73     bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); }
74
75     bool operator==(const FontPlatformDataCacheKey& other) const
76     {
77         return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size && 
78                m_weight == other.m_weight && m_italic == other.m_italic && m_printerFont == other.m_printerFont &&
79                m_renderingMode == other.m_renderingMode && m_orientation == other.m_orientation && m_widthVariant == other.m_widthVariant;
80     }
81
82     unsigned m_size;
83     unsigned m_weight;
84     AtomicString m_family;
85     bool m_italic;
86     bool m_printerFont;
87     FontRenderingMode m_renderingMode;
88     FontOrientation m_orientation;
89     FontWidthVariant m_widthVariant;
90
91 private:
92     static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; }
93 };
94
95 inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
96 {
97     unsigned hashCodes[5] = {
98         CaseFoldingHash::hash(fontKey.m_family),
99         fontKey.m_size,
100         fontKey.m_weight,
101         fontKey.m_widthVariant,
102         static_cast<unsigned>(fontKey.m_orientation) << 3 | static_cast<unsigned>(fontKey.m_italic) << 2 | static_cast<unsigned>(fontKey.m_printerFont) << 1 | static_cast<unsigned>(fontKey.m_renderingMode)
103     };
104     return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes);
105 }
106
107 struct FontPlatformDataCacheKeyHash {
108     static unsigned hash(const FontPlatformDataCacheKey& font)
109     {
110         return computeHash(font);
111     }
112          
113     static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
114     {
115         return a == b;
116     }
117
118     static const bool safeToCompareToEmptyOrDeleted = true;
119 };
120
121 struct FontPlatformDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformDataCacheKey> {
122     static const bool emptyValueIsZero = true;
123     static const FontPlatformDataCacheKey& emptyValue()
124     {
125         DEFINE_STATIC_LOCAL(FontPlatformDataCacheKey, key, (nullAtom));
126         return key;
127     }
128     static void constructDeletedValue(FontPlatformDataCacheKey& slot)
129     {
130         new (&slot) FontPlatformDataCacheKey(HashTableDeletedValue);
131     }
132     static bool isDeletedValue(const FontPlatformDataCacheKey& value)
133     {
134         return value.isHashTableDeletedValue();
135     }
136 };
137
138 typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
139
140 static FontPlatformDataCache* gFontPlatformDataCache = 0;
141
142 static const AtomicString& alternateFamilyName(const AtomicString& familyName)
143 {
144     // Alias Courier <-> Courier New
145     DEFINE_STATIC_LOCAL(AtomicString, courier, ("Courier"));
146     DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New"));
147     if (equalIgnoringCase(familyName, courier))
148         return courierNew;
149 #if !OS(WINDOWS)
150     // On Windows, Courier New (truetype font) is always present and
151     // Courier is a bitmap font. So, we don't want to map Courier New to
152     // Courier.
153     if (equalIgnoringCase(familyName, courierNew))
154         return courier;
155 #endif
156
157     // Alias Times and Times New Roman.
158     DEFINE_STATIC_LOCAL(AtomicString, times, ("Times"));
159     DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, ("Times New Roman"));
160     if (equalIgnoringCase(familyName, times))
161         return timesNewRoman;
162     if (equalIgnoringCase(familyName, timesNewRoman))
163         return times;
164     
165     // Alias Arial and Helvetica
166     DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial"));
167     DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica"));
168     if (equalIgnoringCase(familyName, arial))
169         return helvetica;
170     if (equalIgnoringCase(familyName, helvetica))
171         return arial;
172
173 #if OS(WINDOWS)
174     // On Windows, bitmap fonts are blocked altogether so that we have to 
175     // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font)
176     DEFINE_STATIC_LOCAL(AtomicString, msSans, ("MS Sans Serif"));
177     DEFINE_STATIC_LOCAL(AtomicString, microsoftSans, ("Microsoft Sans Serif"));
178     if (equalIgnoringCase(familyName, msSans))
179         return microsoftSans;
180
181     // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no 
182     // 'Microsoft Sans Serif-equivalent' for Serif. 
183     static AtomicString msSerif("MS Serif");
184     if (equalIgnoringCase(familyName, msSerif))
185         return timesNewRoman;
186 #endif
187
188     return emptyAtom;
189 }
190
191 FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription, 
192                                                        const AtomicString& familyName,
193                                                        bool checkingAlternateName)
194 {
195     if (!gFontPlatformDataCache) {
196         gFontPlatformDataCache = new FontPlatformDataCache;
197         platformInit();
198     }
199
200     FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(),
201                                  fontDescription.usePrinterFont(), fontDescription.renderingMode(), fontDescription.orientation(), fontDescription.widthVariant());
202     FontPlatformData* result = 0;
203     bool foundResult;
204     FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key);
205     if (it == gFontPlatformDataCache->end()) {
206         result = createFontPlatformData(fontDescription, familyName);
207         gFontPlatformDataCache->set(key, result);
208         foundResult = result;
209     } else {
210         result = it->second;
211         foundResult = true;
212     }
213
214     if (!foundResult && !checkingAlternateName) {
215         // We were unable to find a font.  We have a small set of fonts that we alias to other names, 
216         // e.g., Arial/Helvetica, Courier/Courier New, etc.  Try looking up the font under the aliased name.
217         const AtomicString& alternateName = alternateFamilyName(familyName);
218         if (!alternateName.isEmpty())
219             result = getCachedFontPlatformData(fontDescription, alternateName, true);
220         if (result)
221             gFontPlatformDataCache->set(key, new FontPlatformData(*result)); // Cache the result under the old name.
222     }
223
224     return result;
225 }
226
227 struct FontDataCacheKeyHash {
228     static unsigned hash(const FontPlatformData& platformData)
229     {
230         return platformData.hash();
231     }
232          
233     static bool equal(const FontPlatformData& a, const FontPlatformData& b)
234     {
235         return a == b;
236     }
237
238     static const bool safeToCompareToEmptyOrDeleted = true;
239 };
240
241 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
242     static const bool emptyValueIsZero = true;
243     static const bool needsDestruction = true;
244     static const FontPlatformData& emptyValue()
245     {
246         DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false));
247         return key;
248     }
249     static void constructDeletedValue(FontPlatformData& slot)
250     {
251         new (&slot) FontPlatformData(HashTableDeletedValue);
252     }
253     static bool isDeletedValue(const FontPlatformData& value)
254     {
255         return value.isHashTableDeletedValue();
256     }
257 };
258
259 typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
260
261 static FontDataCache* gFontDataCache = 0;
262
263 const int cMaxInactiveFontData = 120;  // Pretty Low Threshold
264 const int cTargetInactiveFontData = 100;
265 static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0;
266
267 SimpleFontData* FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName)
268 {
269     FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName);
270     if (!platformData)
271         return 0;
272
273     return getCachedFontData(platformData);
274 }
275
276 SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData)
277 {
278     if (!platformData)
279         return 0;
280
281     if (!gFontDataCache) {
282         gFontDataCache = new FontDataCache;
283         gInactiveFontData = new ListHashSet<const SimpleFontData*>;
284     }
285     
286     FontDataCache::iterator result = gFontDataCache->find(*platformData);
287     if (result == gFontDataCache->end()) {
288         pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformData), 1);
289         gFontDataCache->set(*platformData, newValue);
290         return newValue.first;
291     }
292     if (!result.get()->second.second++) {
293         ASSERT(gInactiveFontData->contains(result.get()->second.first));
294         gInactiveFontData->remove(result.get()->second.first);
295     }
296
297     return result.get()->second.first;
298 }
299
300 void FontCache::releaseFontData(const SimpleFontData* fontData)
301 {
302     ASSERT(gFontDataCache);
303     ASSERT(!fontData->isCustomFont());
304
305     FontDataCache::iterator it = gFontDataCache->find(fontData->platformData());
306     ASSERT(it != gFontDataCache->end());
307
308     if (!--it->second.second) {
309         gInactiveFontData->add(fontData);
310         if (gInactiveFontData->size() > cMaxInactiveFontData)
311             purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData);
312     }
313 }
314
315 void FontCache::purgeInactiveFontData(int count)
316 {
317     if (!gInactiveFontData)
318         return;
319
320     static bool isPurging;  // Guard against reentry when e.g. a deleted FontData releases its small caps FontData.
321     if (isPurging)
322         return;
323
324     isPurging = true;
325
326     Vector<const SimpleFontData*, 20> fontDataToDelete;
327     ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontData->end();
328     ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontData->begin();
329     for (int i = 0; i < count && it != end; ++it, ++i) {
330         const SimpleFontData* fontData = *it.get();
331         gFontDataCache->remove(fontData->platformData());
332         fontDataToDelete.append(fontData);
333     }
334
335     if (it == end) {
336         // Removed everything
337         gInactiveFontData->clear();
338     } else {
339         for (int i = 0; i < count; ++i)
340             gInactiveFontData->remove(gInactiveFontData->begin());
341     }
342
343     size_t fontDataToDeleteCount = fontDataToDelete.size();
344     for (size_t i = 0; i < fontDataToDeleteCount; ++i)
345         delete fontDataToDelete[i];
346
347     if (gFontPlatformDataCache) {
348         Vector<FontPlatformDataCacheKey> keysToRemove;
349         keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size());
350         FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end();
351         for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) {
352             if (platformData->second && !gFontDataCache->contains(*platformData->second))
353                 keysToRemove.append(platformData->first);
354         }
355         
356         size_t keysToRemoveCount = keysToRemove.size();
357         for (size_t i = 0; i < keysToRemoveCount; ++i)
358             delete gFontPlatformDataCache->take(keysToRemove[i]);
359     }
360
361     isPurging = false;
362 }
363
364 size_t FontCache::fontDataCount()
365 {
366     if (gFontDataCache)
367         return gFontDataCache->size();
368     return 0;
369 }
370
371 size_t FontCache::inactiveFontDataCount()
372 {
373     if (gInactiveFontData)
374         return gInactiveFontData->size();
375     return 0;
376 }
377
378 const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector)
379 {
380     SimpleFontData* result = 0;
381
382     int startIndex = familyIndex;
383     const FontFamily* startFamily = &font.fontDescription().family();
384     for (int i = 0; startFamily && i < startIndex; i++)
385         startFamily = startFamily->next();
386     const FontFamily* currFamily = startFamily;
387     while (currFamily && !result) {
388         familyIndex++;
389         if (currFamily->family().length()) {
390             if (fontSelector) {
391                 FontData* data = fontSelector->getFontData(font.fontDescription(), currFamily->family());
392                 if (data)
393                     return data;
394             }
395             result = getCachedFontData(font.fontDescription(), currFamily->family());
396         }
397         currFamily = currFamily->next();
398     }
399
400     if (!currFamily)
401         familyIndex = cAllFamiliesScanned;
402
403     if (!result)
404         // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
405         // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
406         // Geeza Pro font.
407         result = getSimilarFontPlatformData(font);
408
409     if (!result && startIndex == 0) {
410         // If it's the primary font that we couldn't find, we try the following. In all other cases, we will
411         // just use per-character system fallback.
412
413         if (fontSelector) {
414             // Try the user's preferred standard font.
415             if (FontData* data = fontSelector->getFontData(font.fontDescription(), "-webkit-standard"))
416                 return data;
417         }
418
419         // Still no result.  Hand back our last resort fallback font.
420         result = getLastResortFallbackFont(font.fontDescription());
421     }
422     return result;
423 }
424
425 static HashSet<FontSelector*>* gClients;
426
427 void FontCache::addClient(FontSelector* client)
428 {
429     if (!gClients)
430         gClients = new HashSet<FontSelector*>;
431
432     ASSERT(!gClients->contains(client));
433     gClients->add(client);
434 }
435
436 void FontCache::removeClient(FontSelector* client)
437 {
438     ASSERT(gClients);
439     ASSERT(gClients->contains(client));
440
441     gClients->remove(client);
442 }
443
444 static unsigned gGeneration = 0;
445
446 unsigned FontCache::generation()
447 {
448     return gGeneration;
449 }
450
451 void FontCache::invalidate()
452 {
453     if (!gClients) {
454         ASSERT(!gFontPlatformDataCache);
455         return;
456     }
457
458     if (gFontPlatformDataCache) {
459         deleteAllValues(*gFontPlatformDataCache);
460         delete gFontPlatformDataCache;
461         gFontPlatformDataCache = new FontPlatformDataCache;
462     }
463
464     gGeneration++;
465
466     Vector<RefPtr<FontSelector> > clients;
467     size_t numClients = gClients->size();
468     clients.reserveInitialCapacity(numClients);
469     HashSet<FontSelector*>::iterator end = gClients->end();
470     for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it)
471         clients.append(*it);
472
473     ASSERT(numClients == clients.size());
474     for (size_t i = 0; i < numClients; ++i)
475         clients[i]->fontCacheInvalidated();
476
477     purgeInactiveFontData();
478 }
479
480 } // namespace WebCore