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