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