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 "platform/fonts/FontCache.h" 32 33#include "FontFamilyNames.h" 34 35#include "RuntimeEnabledFeatures.h" 36#include "platform/fonts/AlternateFontFamily.h" 37#include "platform/fonts/FontCacheKey.h" 38#include "platform/fonts/FontDataCache.h" 39#include "platform/fonts/FontDescription.h" 40#include "platform/fonts/FontFallbackList.h" 41#include "platform/fonts/FontPlatformData.h" 42#include "platform/fonts/FontSelector.h" 43#include "platform/fonts/FontSmoothingMode.h" 44#include "platform/fonts/TextRenderingMode.h" 45#include "platform/fonts/opentype/OpenTypeVerticalData.h" 46#include "wtf/HashMap.h" 47#include "wtf/ListHashSet.h" 48#include "wtf/StdLibExtras.h" 49#include "wtf/text/AtomicStringHash.h" 50#include "wtf/text/StringHash.h" 51 52using namespace WTF; 53 54namespace WebCore { 55 56#if !OS(WIN) || ENABLE(GDI_FONTS_ON_WINDOWS) 57FontCache::FontCache() 58 : m_purgePreventCount(0) 59{ 60} 61#endif // !OS(WIN) || ENABLE(GDI_FONTS_ON_WINDOWS) 62 63typedef HashMap<FontCacheKey, OwnPtr<FontPlatformData>, FontCacheKeyHash, FontCacheKeyTraits> FontPlatformDataCache; 64 65static FontPlatformDataCache* gFontPlatformDataCache = 0; 66 67FontCache* FontCache::fontCache() 68{ 69 DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ()); 70 return &globalFontCache; 71} 72 73FontPlatformData* FontCache::getFontPlatformData(const FontDescription& fontDescription, 74 const AtomicString& passedFamilyName, bool checkingAlternateName) 75{ 76#if OS(WIN) && ENABLE(OPENTYPE_VERTICAL) 77 // Leading "@" in the font name enables Windows vertical flow flag for the font. 78 // Because we do vertical flow by ourselves, we don't want to use the Windows feature. 79 // IE disregards "@" regardless of the orientatoin, so we follow the behavior. 80 const AtomicString& familyName = (passedFamilyName.isEmpty() || passedFamilyName[0] != '@') ? 81 passedFamilyName : AtomicString(passedFamilyName.impl()->substring(1)); 82#else 83 const AtomicString& familyName = passedFamilyName; 84#endif 85 86 if (!gFontPlatformDataCache) { 87 gFontPlatformDataCache = new FontPlatformDataCache; 88 platformInit(); 89 } 90 91 FontCacheKey key = fontDescription.cacheKey(familyName); 92 FontPlatformData* result = 0; 93 bool foundResult; 94 FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key); 95 if (it == gFontPlatformDataCache->end()) { 96 result = createFontPlatformData(fontDescription, familyName, fontDescription.effectiveFontSize()); 97 gFontPlatformDataCache->set(key, adoptPtr(result)); 98 foundResult = result; 99 } else { 100 result = it->value.get(); 101 foundResult = true; 102 } 103 104 if (!foundResult && !checkingAlternateName) { 105 // We were unable to find a font. We have a small set of fonts that we alias to other names, 106 // e.g., Arial/Helvetica, Courier/Courier New, etc. Try looking up the font under the aliased name. 107 const AtomicString& alternateName = alternateFamilyName(familyName); 108 if (!alternateName.isEmpty()) 109 result = getFontPlatformData(fontDescription, alternateName, true); 110 if (result) 111 gFontPlatformDataCache->set(key, adoptPtr(new FontPlatformData(*result))); // Cache the result under the old name. 112 } 113 114 return result; 115} 116 117#if ENABLE(OPENTYPE_VERTICAL) 118typedef HashMap<FontCache::FontFileKey, RefPtr<OpenTypeVerticalData>, WTF::IntHash<FontCache::FontFileKey>, WTF::UnsignedWithZeroKeyHashTraits<FontCache::FontFileKey> > FontVerticalDataCache; 119 120FontVerticalDataCache& fontVerticalDataCacheInstance() 121{ 122 DEFINE_STATIC_LOCAL(FontVerticalDataCache, fontVerticalDataCache, ()); 123 return fontVerticalDataCache; 124} 125 126PassRefPtr<OpenTypeVerticalData> FontCache::getVerticalData(const FontFileKey& key, const FontPlatformData& platformData) 127{ 128 FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance(); 129 FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key); 130 if (result != fontVerticalDataCache.end()) 131 return result.get()->value; 132 133 RefPtr<OpenTypeVerticalData> verticalData = OpenTypeVerticalData::create(platformData); 134 if (!verticalData->isOpenType()) 135 verticalData.clear(); 136 fontVerticalDataCache.set(key, verticalData); 137 return verticalData; 138} 139#endif 140 141static FontDataCache* gFontDataCache = 0; 142 143PassRefPtr<SimpleFontData> FontCache::getFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName, ShouldRetain shouldRetain) 144{ 145 if (FontPlatformData* platformData = getFontPlatformData(fontDescription, adjustFamilyNameToAvoidUnsupportedFonts(family), checkingAlternateName)) 146 return fontDataFromFontPlatformData(platformData, shouldRetain); 147 148 return 0; 149} 150 151PassRefPtr<SimpleFontData> FontCache::fontDataFromFontPlatformData(const FontPlatformData* platformData, ShouldRetain shouldRetain) 152{ 153 if (!gFontDataCache) 154 gFontDataCache = new FontDataCache; 155 156#if !ASSERT_DISABLED 157 if (shouldRetain == DoNotRetain) 158 ASSERT(m_purgePreventCount); 159#endif 160 161 return gFontDataCache->get(platformData, shouldRetain); 162} 163 164bool FontCache::isPlatformFontAvailable(const FontDescription& fontDescription, const AtomicString& family) 165{ 166 bool checkingAlternateName = true; 167 return getFontPlatformData(fontDescription, family, checkingAlternateName); 168} 169 170SimpleFontData* FontCache::getNonRetainedLastResortFallbackFont(const FontDescription& fontDescription) 171{ 172 return getLastResortFallbackFont(fontDescription, DoNotRetain).leakRef(); 173} 174 175void FontCache::releaseFontData(const SimpleFontData* fontData) 176{ 177 ASSERT(gFontDataCache); 178 179 gFontDataCache->release(fontData); 180} 181 182static inline void purgePlatformFontDataCache() 183{ 184 if (!gFontPlatformDataCache) 185 return; 186 187 Vector<FontCacheKey> keysToRemove; 188 keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); 189 FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); 190 for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { 191 if (platformData->value && !gFontDataCache->contains(platformData->value.get())) 192 keysToRemove.append(platformData->key); 193 } 194 195 size_t keysToRemoveCount = keysToRemove.size(); 196 for (size_t i = 0; i < keysToRemoveCount; ++i) 197 gFontPlatformDataCache->remove(keysToRemove[i]); 198} 199 200static inline void purgeFontVerticalDataCache() 201{ 202#if ENABLE(OPENTYPE_VERTICAL) 203 FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance(); 204 if (!fontVerticalDataCache.isEmpty()) { 205 // Mark & sweep unused verticalData 206 FontVerticalDataCache::iterator verticalDataEnd = fontVerticalDataCache.end(); 207 for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) { 208 if (verticalData->value) 209 verticalData->value->setInFontCache(false); 210 } 211 212 gFontDataCache->markAllVerticalData(); 213 214 Vector<FontCache::FontFileKey> keysToRemove; 215 keysToRemove.reserveInitialCapacity(fontVerticalDataCache.size()); 216 for (FontVerticalDataCache::iterator verticalData = fontVerticalDataCache.begin(); verticalData != verticalDataEnd; ++verticalData) { 217 if (!verticalData->value || !verticalData->value->inFontCache()) 218 keysToRemove.append(verticalData->key); 219 } 220 for (size_t i = 0, count = keysToRemove.size(); i < count; ++i) 221 fontVerticalDataCache.take(keysToRemove[i]); 222 } 223#endif 224} 225 226void FontCache::purge(PurgeSeverity PurgeSeverity) 227{ 228 // We should never be forcing the purge while the FontCachePurgePreventer is in scope. 229 ASSERT(!m_purgePreventCount || PurgeSeverity == PurgeIfNeeded); 230 if (m_purgePreventCount) 231 return; 232 233 if (!gFontDataCache || !gFontDataCache->purge(PurgeSeverity)) 234 return; 235 236 purgePlatformFontDataCache(); 237 purgeFontVerticalDataCache(); 238} 239 240static HashSet<FontSelector*>* gClients; 241 242void FontCache::addClient(FontSelector* client) 243{ 244 if (!gClients) 245 gClients = new HashSet<FontSelector*>; 246 247 ASSERT(!gClients->contains(client)); 248 gClients->add(client); 249} 250 251void FontCache::removeClient(FontSelector* client) 252{ 253 ASSERT(gClients); 254 ASSERT(gClients->contains(client)); 255 256 gClients->remove(client); 257} 258 259static unsigned short gGeneration = 0; 260 261unsigned short FontCache::generation() 262{ 263 return gGeneration; 264} 265 266void FontCache::invalidate() 267{ 268 if (!gClients) { 269 ASSERT(!gFontPlatformDataCache); 270 return; 271 } 272 273 if (gFontPlatformDataCache) { 274 delete gFontPlatformDataCache; 275 gFontPlatformDataCache = new FontPlatformDataCache; 276 } 277 278 gGeneration++; 279 280 Vector<RefPtr<FontSelector> > clients; 281 size_t numClients = gClients->size(); 282 clients.reserveInitialCapacity(numClients); 283 HashSet<FontSelector*>::iterator end = gClients->end(); 284 for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it) 285 clients.append(*it); 286 287 ASSERT(numClients == clients.size()); 288 for (size_t i = 0; i < numClients; ++i) 289 clients[i]->fontCacheInvalidated(); 290 291 purge(ForcePurge); 292} 293 294} // namespace WebCore 295