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