Rename FontDescriptionFontDataCacheKey to FontDescriptionKey
[WebKit-https.git] / Source / WebCore / platform / graphics / FontCache.cpp
index 6cc130f..368c4b9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2008, 2013 Apple Inc. All rights reserved.
  * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -11,7 +11,7 @@
  * 2.  Redistributions in binary form must reproduce the above copyright
  *     notice, this list of conditions and the following disclaimer in the
  *     documentation and/or other materials provided with the distribution. 
- * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
  *     its contributors may be used to endorse or promote products derived
  *     from this software without specific prior written permission. 
  *
 #include "config.h"
 #include "FontCache.h"
 
-#include "Font.h"
-#include "FontFallbackList.h"
+#include "FontCascade.h"
 #include "FontPlatformData.h"
 #include "FontSelector.h"
-#include "GlyphPageTreeNode.h"
+#include "MemoryPressureHandler.h"
 #include "WebKitFontFamilyNames.h"
 #include <wtf/HashMap.h>
 #include <wtf/ListHashSet.h>
+#include <wtf/NeverDestroyed.h>
 #include <wtf/StdLibExtras.h>
+#include <wtf/TemporaryChange.h>
+#include <wtf/text/AtomicStringHash.h>
 #include <wtf/text/StringHash.h>
 
+#if ENABLE(OPENTYPE_VERTICAL)
+#include "OpenTypeVerticalData.h"
+#endif
+
+#if PLATFORM(IOS)
+#include <wtf/Noncopyable.h>
+
+// FIXME: We may be able to simplify this code using C++11 threading primitives, including std::call_once().
+static pthread_mutex_t fontLock;
+
+static void initFontCacheLockOnce()
+{
+    pthread_mutexattr_t mutexAttribute;
+    pthread_mutexattr_init(&mutexAttribute);
+    pthread_mutexattr_settype(&mutexAttribute, PTHREAD_MUTEX_RECURSIVE);
+    pthread_mutex_init(&fontLock, &mutexAttribute);
+    pthread_mutexattr_destroy(&mutexAttribute);
+}
+
+static pthread_once_t initFontLockControl = PTHREAD_ONCE_INIT;
+
+class FontLocker {
+    WTF_MAKE_NONCOPYABLE(FontLocker);
+public:
+    FontLocker()
+    {
+        pthread_once(&initFontLockControl, initFontCacheLockOnce);
+        int lockcode = pthread_mutex_lock(&fontLock);
+        ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontLock lock failed with code:%d", lockcode);    
+    }
+    ~FontLocker()
+    {
+        int lockcode = pthread_mutex_unlock(&fontLock);
+        ASSERT_WITH_MESSAGE_UNUSED(lockcode, !lockcode, "fontLock unlock failed with code:%d", lockcode);
+    }
+};
+#endif // PLATFORM(IOS)
+
 using namespace WTF;
 
 namespace WebCore {
 
-FontCache* fontCache()
+FontCache& FontCache::singleton()
 {
-    DEFINE_STATIC_LOCAL(FontCache, globalFontCache, ());
-    return &globalFontCache;
+    static NeverDestroyed<FontCache> globalFontCache;
+    return globalFontCache;
 }
 
 FontCache::FontCache()
-    : m_purgePreventCount(0)
+    : m_purgeTimer(*this, &FontCache::purgeTimerFired)
 {
 }
 
 struct FontPlatformDataCacheKey {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false,
-                             bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode, FontOrientation orientation = Horizontal,
-                             TextOrientation textOrientation = TextOrientationVerticalRight, FontWidthVariant widthVariant = RegularWidth)
-        : m_size(size)
-        , m_weight(weight)
+    FontPlatformDataCacheKey() { }
+    FontPlatformDataCacheKey(const AtomicString& family, const FontDescription& description)
+        : m_fontDescriptionKey(description)
         , m_family(family)
-        , m_italic(italic)
-        , m_printerFont(isPrinterFont)
-        , m_renderingMode(renderingMode)
-        , m_orientation(orientation)
-        , m_textOrientation(textOrientation)
-        , m_widthVariant(widthVariant)
-    {
-    }
+    { }
 
-    FontPlatformDataCacheKey(HashTableDeletedValueType) : m_size(hashTableDeletedSize()) { }
-    bool isHashTableDeletedValue() const { return m_size == hashTableDeletedSize(); }
+    FontPlatformDataCacheKey(HashTableDeletedValueType) : m_fontDescriptionKey(hashTableDeletedSize()) { }
+    bool isHashTableDeletedValue() const { return m_fontDescriptionKey.size == hashTableDeletedSize(); }
 
     bool operator==(const FontPlatformDataCacheKey& other) const
     {
-        return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size && 
-               m_weight == other.m_weight && m_italic == other.m_italic && m_printerFont == other.m_printerFont &&
-               m_renderingMode == other.m_renderingMode && m_orientation == other.m_orientation && m_textOrientation == other.m_textOrientation && m_widthVariant == other.m_widthVariant;
+        return equalIgnoringCase(m_family, other.m_family) && m_fontDescriptionKey == other.m_fontDescriptionKey;
     }
 
-    unsigned m_size;
-    unsigned m_weight;
+    FontDescriptionKey m_fontDescriptionKey;
     AtomicString m_family;
-    bool m_italic;
-    bool m_printerFont;
-    FontRenderingMode m_renderingMode;
-    FontOrientation m_orientation;
-    TextOrientation m_textOrientation;
-    FontWidthVariant m_widthVariant;
 
 private:
     static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; }
 };
 
-inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey)
-{
-    unsigned hashCodes[5] = {
-        CaseFoldingHash::hash(fontKey.m_family),
-        fontKey.m_size,
-        fontKey.m_weight,
-        fontKey.m_widthVariant,
-        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)
-    };
-    return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes);
-}
-
 struct FontPlatformDataCacheKeyHash {
-    static unsigned hash(const FontPlatformDataCacheKey& font)
+    static unsigned hash(const FontPlatformDataCacheKey& fontKey)
     {
-        return computeHash(font);
+        return pairIntHash(CaseFoldingHash::hash(fontKey.m_family), fontKey.m_fontDescriptionKey.computeHash());
     }
          
     static bool equal(const FontPlatformDataCacheKey& a, const FontPlatformDataCacheKey& b)
@@ -124,97 +134,190 @@ struct FontPlatformDataCacheKeyHash {
     static const bool safeToCompareToEmptyOrDeleted = true;
 };
 
-struct FontPlatformDataCacheKeyTraits : WTF::SimpleClassHashTraits<FontPlatformDataCacheKey> { };
+typedef HashMap<FontPlatformDataCacheKey, std::unique_ptr<FontPlatformData>, FontPlatformDataCacheKeyHash, WTF::SimpleClassHashTraits<FontPlatformDataCacheKey>> FontPlatformDataCache;
 
-typedef HashMap<FontPlatformDataCacheKey, FontPlatformData*, FontPlatformDataCacheKeyHash, FontPlatformDataCacheKeyTraits> FontPlatformDataCache;
+static FontPlatformDataCache& fontPlatformDataCache()
+{
+    static NeverDestroyed<FontPlatformDataCache> cache;
+    return cache;
+}
 
-static FontPlatformDataCache* gFontPlatformDataCache = 0;
+static bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char* reference, unsigned length)
+{
+    ASSERT(length > 0);
+    ASSERT(familyName.length() == length);
+    ASSERT(strlen(reference) == length);
+    const AtomicStringImpl* familyNameImpl = familyName.impl();
+    if (familyNameImpl->is8Bit())
+        return equalIgnoringCase(familyNameImpl->characters8(), reinterpret_cast<const LChar*>(reference), length);
+    return equalIgnoringCase(familyNameImpl->characters16(), reinterpret_cast<const LChar*>(reference), length);
+}
+
+template<size_t length>
+static inline bool familyNameEqualIgnoringCase(const AtomicString& familyName, const char (&reference)[length])
+{
+    return familyNameEqualIgnoringCase(familyName, reference, length - 1);
+}
 
-static const AtomicString& alternateFamilyName(const AtomicString& familyName)
+static const AtomicString alternateFamilyName(const AtomicString& familyName)
 {
-    // Alias Courier <-> Courier New
-    DEFINE_STATIC_LOCAL(AtomicString, courier, ("Courier"));
-    DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New"));
-    if (equalIgnoringCase(familyName, courier))
-        return courierNew;
+    // Alias Courier and Courier New.
+    // Alias Times and Times New Roman.
+    // Alias Arial and Helvetica.
+    switch (familyName.length()) {
+    case 5:
+        if (familyNameEqualIgnoringCase(familyName, "Arial"))
+            return AtomicString("Helvetica", AtomicString::ConstructFromLiteral);
+        if (familyNameEqualIgnoringCase(familyName, "Times"))
+            return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
+        break;
+    case 7:
+        if (familyNameEqualIgnoringCase(familyName, "Courier"))
+            return AtomicString("Courier New", AtomicString::ConstructFromLiteral);
+        break;
+    case 9:
+        if (familyNameEqualIgnoringCase(familyName, "Helvetica"))
+            return AtomicString("Arial", AtomicString::ConstructFromLiteral);
+        break;
 #if !OS(WINDOWS)
     // On Windows, Courier New (truetype font) is always present and
     // Courier is a bitmap font. So, we don't want to map Courier New to
     // Courier.
-    if (equalIgnoringCase(familyName, courierNew))
-        return courier;
-#endif
-
-    // Alias Times and Times New Roman.
-    DEFINE_STATIC_LOCAL(AtomicString, times, ("Times"));
-    DEFINE_STATIC_LOCAL(AtomicString, timesNewRoman, ("Times New Roman"));
-    if (equalIgnoringCase(familyName, times))
-        return timesNewRoman;
-    if (equalIgnoringCase(familyName, timesNewRoman))
-        return times;
-    
-    // Alias Arial and Helvetica
-    DEFINE_STATIC_LOCAL(AtomicString, arial, ("Arial"));
-    DEFINE_STATIC_LOCAL(AtomicString, helvetica, ("Helvetica"));
-    if (equalIgnoringCase(familyName, arial))
-        return helvetica;
-    if (equalIgnoringCase(familyName, helvetica))
-        return arial;
-
+    case 11:
+        if (familyNameEqualIgnoringCase(familyName, "Courier New"))
+            return AtomicString("Courier", AtomicString::ConstructFromLiteral);
+        break;
+#endif // !OS(WINDOWS)
+    case 15:
+        if (familyNameEqualIgnoringCase(familyName, "Times New Roman"))
+            return AtomicString("Times", AtomicString::ConstructFromLiteral);
+        break;
 #if OS(WINDOWS)
     // On Windows, bitmap fonts are blocked altogether so that we have to 
     // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font)
-    DEFINE_STATIC_LOCAL(AtomicString, msSans, ("MS Sans Serif"));
-    DEFINE_STATIC_LOCAL(AtomicString, microsoftSans, ("Microsoft Sans Serif"));
-    if (equalIgnoringCase(familyName, msSans))
-        return microsoftSans;
+    case 13:
+        if (familyNameEqualIgnoringCase(familyName, "MS Sans Serif"))
+            return AtomicString("Microsoft Sans Serif", AtomicString::ConstructFromLiteral);
+        break;
 
     // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no 
-    // 'Microsoft Sans Serif-equivalent' for Serif. 
-    static AtomicString msSerif("MS Serif");
-    if (equalIgnoringCase(familyName, msSerif))
-        return timesNewRoman;
-#endif
+    // 'Microsoft Sans Serif-equivalent' for Serif.
+    case 8:
+        if (familyNameEqualIgnoringCase(familyName, "MS Serif"))
+            return AtomicString("Times New Roman", AtomicString::ConstructFromLiteral);
+        break;
+#endif // OS(WINDOWS)
 
-    return emptyAtom;
+    }
+
+    return nullAtom;
 }
 
-FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription, 
-                                                       const AtomicString& familyName,
+FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fontDescription,
+                                                       const AtomicString& passedFamilyName,
                                                        bool checkingAlternateName)
 {
-    if (!gFontPlatformDataCache) {
-        gFontPlatformDataCache = new FontPlatformDataCache;
+#if PLATFORM(IOS)
+    FontLocker fontLocker;
+#endif
+    
+#if OS(WINDOWS) && ENABLE(OPENTYPE_VERTICAL)
+    // Leading "@" in the font name enables Windows vertical flow flag for the font.
+    // Because we do vertical flow by ourselves, we don't want to use the Windows feature.
+    // IE disregards "@" regardless of the orientatoin, so we follow the behavior.
+    const AtomicString& familyName = (passedFamilyName.isEmpty() || passedFamilyName[0] != '@') ?
+        passedFamilyName : AtomicString(passedFamilyName.impl()->substring(1));
+#else
+    const AtomicString& familyName = passedFamilyName;
+#endif
+
+    static bool initialized;
+    if (!initialized) {
         platformInit();
+        initialized = true;
     }
 
-    FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(),
-                                 fontDescription.usePrinterFont(), fontDescription.renderingMode(), fontDescription.orientation(),
-                                 fontDescription.textOrientation(), fontDescription.widthVariant());
-    FontPlatformData* result = 0;
-    bool foundResult;
-    FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key);
-    if (it == gFontPlatformDataCache->end()) {
-        result = createFontPlatformData(fontDescription, familyName);
-        gFontPlatformDataCache->set(key, result);
-        foundResult = result;
-    } else {
-        result = it->second;
-        foundResult = true;
+    FontPlatformDataCacheKey key(familyName, fontDescription);
+
+    auto addResult = fontPlatformDataCache().add(key, nullptr);
+    FontPlatformDataCache::iterator it = addResult.iterator;
+    if (addResult.isNewEntry) {
+        it->value = createFontPlatformData(fontDescription, familyName);
+
+        if (!it->value && !checkingAlternateName) {
+            // We were unable to find a font.  We have a small set of fonts that we alias to other names,
+            // e.g., Arial/Helvetica, Courier/Courier New, etc.  Try looking up the font under the aliased name.
+            const AtomicString alternateName = alternateFamilyName(familyName);
+            if (!alternateName.isNull()) {
+                FontPlatformData* fontPlatformDataForAlternateName = getCachedFontPlatformData(fontDescription, alternateName, true);
+                // Lookup the key in the hash table again as the previous iterator may have
+                // been invalidated by the recursive call to getCachedFontPlatformData().
+                it = fontPlatformDataCache().find(key);
+                ASSERT(it != fontPlatformDataCache().end());
+                if (fontPlatformDataForAlternateName)
+                    it->value = std::make_unique<FontPlatformData>(*fontPlatformDataForAlternateName);
+            }
+        }
     }
 
-    if (!foundResult && !checkingAlternateName) {
-        // We were unable to find a font.  We have a small set of fonts that we alias to other names, 
-        // e.g., Arial/Helvetica, Courier/Courier New, etc.  Try looking up the font under the aliased name.
-        const AtomicString& alternateName = alternateFamilyName(familyName);
-        if (!alternateName.isEmpty())
-            result = getCachedFontPlatformData(fontDescription, alternateName, true);
-        if (result)
-            gFontPlatformDataCache->set(key, new FontPlatformData(*result)); // Cache the result under the old name.
+    return it->value.get();
+}
+
+#if ENABLE(OPENTYPE_VERTICAL)
+struct FontVerticalDataCacheKeyHash {
+    static unsigned hash(const FontCache::FontFileKey& fontFileKey)
+    {
+        return PtrHash<const FontCache::FontFileKey*>::hash(&fontFileKey);
     }
 
-    return result;
+    static bool equal(const FontCache::FontFileKey& a, const FontCache::FontFileKey& b)
+    {
+        return a == b;
+    }
+
+    static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+struct FontVerticalDataCacheKeyTraits : WTF::GenericHashTraits<FontCache::FontFileKey> {
+    static const bool emptyValueIsZero = true;
+    static const bool needsDestruction = true;
+    static const FontCache::FontFileKey& emptyValue()
+    {
+        static NeverDestroyed<FontCache::FontFileKey> key = nullAtom;
+        return key;
+    }
+    static void constructDeletedValue(FontCache::FontFileKey& slot)
+    {
+        new (NotNull, &slot) FontCache::FontFileKey(HashTableDeletedValue);
+    }
+    static bool isDeletedValue(const FontCache::FontFileKey& value)
+    {
+        return value.isHashTableDeletedValue();
+    }
+};
+
+typedef HashMap<FontCache::FontFileKey, RefPtr<OpenTypeVerticalData>, FontVerticalDataCacheKeyHash, FontVerticalDataCacheKeyTraits> FontVerticalDataCache;
+
+FontVerticalDataCache& fontVerticalDataCacheInstance()
+{
+    static NeverDestroyed<FontVerticalDataCache> fontVerticalDataCache;
+    return fontVerticalDataCache;
+}
+
+PassRefPtr<OpenTypeVerticalData> FontCache::getVerticalData(const FontFileKey& key, const FontPlatformData& platformData)
+{
+    FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance();
+    FontVerticalDataCache::iterator result = fontVerticalDataCache.find(key);
+    if (result != fontVerticalDataCache.end())
+        return result.get()->value;
+
+    RefPtr<OpenTypeVerticalData> verticalData = OpenTypeVerticalData::create(platformData);
+    if (!verticalData->isOpenType())
+        verticalData = nullptr;
+    fontVerticalDataCache.set(key, verticalData);
+    return verticalData;
 }
+#endif
 
 struct FontDataCacheKeyHash {
     static unsigned hash(const FontPlatformData& platformData)
@@ -232,15 +335,14 @@ struct FontDataCacheKeyHash {
 
 struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
     static const bool emptyValueIsZero = true;
-    static const bool needsDestruction = true;
     static const FontPlatformData& emptyValue()
     {
-        DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false));
+        static NeverDestroyed<FontPlatformData> key(0.f, false, false);
         return key;
     }
     static void constructDeletedValue(FontPlatformData& slot)
     {
-        new (&slot) FontPlatformData(HashTableDeletedValue);
+        new (NotNull, &slot) FontPlatformData(HashTableDeletedValue);
     }
     static bool isDeletedValue(const FontPlatformData& value)
     {
@@ -248,195 +350,146 @@ struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
     }
 };
 
-typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
+typedef HashMap<FontPlatformData, RefPtr<Font>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
+
+static FontDataCache& cachedFonts()
+{
+    static NeverDestroyed<FontDataCache> cache;
+    return cache;
+}
 
-static FontDataCache* gFontDataCache = 0;
 
-#if PLATFORM(CHROMIUM)
-const int cMaxInactiveFontData = 250;
-const int cTargetInactiveFontData = 200;
+#if PLATFORM(IOS)
+const unsigned cMaxInactiveFontData = 120;
+const unsigned cTargetInactiveFontData = 100;
 #else
-const int cMaxInactiveFontData = 50; // Pretty Low Threshold
-const int cTargetInactiveFontData = 30;
+const unsigned cMaxInactiveFontData = 225;
+const unsigned cTargetInactiveFontData = 200;
 #endif
-static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0;
 
-SimpleFontData* FontCache::getCachedFontData(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName, ShouldRetain shouldRetain)
+const unsigned cMaxUnderMemoryPressureInactiveFontData = 50;
+const unsigned cTargetUnderMemoryPressureInactiveFontData = 30;
+
+RefPtr<Font> FontCache::fontForFamily(const FontDescription& fontDescription, const AtomicString& family, bool checkingAlternateName)
 {
+    if (!m_purgeTimer.isActive())
+        m_purgeTimer.startOneShot(std::chrono::milliseconds::zero());
+
     FontPlatformData* platformData = getCachedFontPlatformData(fontDescription, family, checkingAlternateName);
     if (!platformData)
-        return 0;
+        return nullptr;
 
-    return getCachedFontData(platformData, shouldRetain);
+    return fontForPlatformData(*platformData);
 }
 
-SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData, ShouldRetain shouldRetain)
+Ref<Font> FontCache::fontForPlatformData(const FontPlatformData& platformData)
 {
-    if (!platformData)
-        return 0;
-
-#if !ASSERT_DISABLED
-    if (shouldRetain == DoNotRetain)
-        ASSERT(m_purgePreventCount);
+#if PLATFORM(IOS)
+    FontLocker fontLocker;
 #endif
+    
+    auto addResult = cachedFonts().add(platformData, nullptr);
+    if (addResult.isNewEntry)
+        addResult.iterator->value = Font::create(platformData);
 
-    if (!gFontDataCache) {
-        gFontDataCache = new FontDataCache;
-        gInactiveFontData = new ListHashSet<const SimpleFontData*>;
-    }
-
-    FontDataCache::iterator result = gFontDataCache->find(*platformData);
-    if (result == gFontDataCache->end()) {
-        pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformData), shouldRetain == Retain ? 1 : 0);
-        gFontDataCache->set(*platformData, newValue);
-        if (shouldRetain == DoNotRetain)
-            gInactiveFontData->add(newValue.first);
-        return newValue.first;
-    }
-
-    if (!result.get()->second.second) {
-        ASSERT(gInactiveFontData->contains(result.get()->second.first));
-        gInactiveFontData->remove(result.get()->second.first);
-    }
-
-    if (shouldRetain == Retain)
-        result.get()->second.second++;
-    else if (!result.get()->second.second) {
-        // If shouldRetain is DoNotRetain and count is 0, we want to remove the fontData from 
-        // gInactiveFontData (above) and re-add here to update LRU position.
-        gInactiveFontData->add(result.get()->second.first);
-    }
-
-    return result.get()->second.first;
+    return *addResult.iterator->value;
 }
 
-void FontCache::releaseFontData(const SimpleFontData* fontData)
+void FontCache::purgeTimerFired()
 {
-    ASSERT(gFontDataCache);
-    ASSERT(!fontData->isCustomFont());
-
-    FontDataCache::iterator it = gFontDataCache->find(fontData->platformData());
-    ASSERT(it != gFontDataCache->end());
-
-    ASSERT(it->second.second);
-    if (!--it->second.second)
-        gInactiveFontData->add(fontData);
+    purgeInactiveFontDataIfNeeded();
 }
 
 void FontCache::purgeInactiveFontDataIfNeeded()
 {
-    if (gInactiveFontData && !m_purgePreventCount && gInactiveFontData->size() > cMaxInactiveFontData)
-        purgeInactiveFontData(gInactiveFontData->size() - cTargetInactiveFontData);
-}
+    bool underMemoryPressure = MemoryPressureHandler::singleton().isUnderMemoryPressure();
+    unsigned inactiveFontDataLimit = underMemoryPressure ? cMaxUnderMemoryPressureInactiveFontData : cMaxInactiveFontData;
 
-void FontCache::purgeInactiveFontData(int count)
-{
-    if (!gInactiveFontData || m_purgePreventCount)
+    if (cachedFonts().size() < inactiveFontDataLimit)
         return;
-
-    static bool isPurging;  // Guard against reentry when e.g. a deleted FontData releases its small caps FontData.
-    if (isPurging)
+    unsigned inactiveCount = inactiveFontCount();
+    if (inactiveCount <= inactiveFontDataLimit)
         return;
 
-    isPurging = true;
+    unsigned targetFontDataLimit = underMemoryPressure ? cTargetUnderMemoryPressureInactiveFontData : cTargetInactiveFontData;
+    purgeInactiveFontData(inactiveCount - targetFontDataLimit);
+}
 
-    Vector<const SimpleFontData*, 20> fontDataToDelete;
-    ListHashSet<const SimpleFontData*>::iterator end = gInactiveFontData->end();
-    ListHashSet<const SimpleFontData*>::iterator it = gInactiveFontData->begin();
-    for (int i = 0; i < count && it != end; ++it, ++i) {
-        const SimpleFontData* fontData = *it.get();
-        gFontDataCache->remove(fontData->platformData());
-        // We should not delete SimpleFontData here because deletion can modify gInactiveFontData. See http://trac.webkit.org/changeset/44011
-        fontDataToDelete.append(fontData);
-    }
+void FontCache::purgeInactiveFontData(unsigned purgeCount)
+{
+    pruneUnreferencedEntriesFromFontCascadeCache();
+    pruneSystemFallbackFonts();
 
-    if (it == end) {
-        // Removed everything
-        gInactiveFontData->clear();
-    } else {
-        for (int i = 0; i < count; ++i)
-            gInactiveFontData->remove(gInactiveFontData->begin());
-    }
+#if PLATFORM(IOS)
+    FontLocker fontLocker;
+#endif
 
-    size_t fontDataToDeleteCount = fontDataToDelete.size();
-    for (size_t i = 0; i < fontDataToDeleteCount; ++i)
-        delete fontDataToDelete[i];
-
-    if (gFontPlatformDataCache) {
-        Vector<FontPlatformDataCacheKey> keysToRemove;
-        keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size());
-        FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end();
-        for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) {
-            if (platformData->second && !gFontDataCache->contains(*platformData->second))
-                keysToRemove.append(platformData->first);
+    while (purgeCount) {
+        Vector<RefPtr<Font>, 20> fontsToDelete;
+        for (auto& font : cachedFonts().values()) {
+            if (!font->hasOneRef())
+                continue;
+            fontsToDelete.append(WTF::move(font));
+            if (!--purgeCount)
+                break;
         }
-        
-        size_t keysToRemoveCount = keysToRemove.size();
-        for (size_t i = 0; i < keysToRemoveCount; ++i)
-            delete gFontPlatformDataCache->take(keysToRemove[i]);
-    }
-
-    isPurging = false;
-}
+        // Fonts may ref other fonts so we loop until there are no changes.
+        if (fontsToDelete.isEmpty())
+            break;
+        for (auto& font : fontsToDelete)
+            cachedFonts().remove(font->platformData());
+    };
 
-size_t FontCache::fontDataCount()
-{
-    if (gFontDataCache)
-        return gFontDataCache->size();
-    return 0;
+    Vector<FontPlatformDataCacheKey> keysToRemove;
+    keysToRemove.reserveInitialCapacity(fontPlatformDataCache().size());
+    for (auto& entry : fontPlatformDataCache()) {
+        if (entry.value && !cachedFonts().contains(*entry.value))
+            keysToRemove.append(entry.key);
+    }
+    for (auto key : keysToRemove)
+        fontPlatformDataCache().remove(key);
+
+#if ENABLE(OPENTYPE_VERTICAL)
+    FontVerticalDataCache& fontVerticalDataCache = fontVerticalDataCacheInstance();
+    if (!fontVerticalDataCache.isEmpty()) {
+        // Mark & sweep unused verticalData
+        for (auto& verticalData : fontVerticalDataCache.values()) {
+            if (verticalData)
+                verticalData->m_inFontCache = false;
+        }
+        for (auto& font : cachedFonts().values()) {
+            auto* verticalData = const_cast<OpenTypeVerticalData*>(font->verticalData());
+            if (verticalData)
+                verticalData->m_inFontCache = true;
+        }
+        Vector<FontFileKey> keysToRemove;
+        keysToRemove.reserveInitialCapacity(fontVerticalDataCache.size());
+        for (auto& it : fontVerticalDataCache) {
+            if (!it.value || !it.value->m_inFontCache)
+                keysToRemove.append(it.key);
+        }
+        for (auto& key : keysToRemove)
+            fontVerticalDataCache.remove(key);
+    }
+#endif
 }
 
-size_t FontCache::inactiveFontDataCount()
+size_t FontCache::fontCount()
 {
-    if (gInactiveFontData)
-        return gInactiveFontData->size();
-    return 0;
+    return cachedFonts().size();
 }
 
-const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector)
+size_t FontCache::inactiveFontCount()
 {
-    FontData* result = 0;
-
-    int startIndex = familyIndex;
-    const FontFamily* startFamily = &font.fontDescription().family();
-    for (int i = 0; startFamily && i < startIndex; i++)
-        startFamily = startFamily->next();
-    const FontFamily* currFamily = startFamily;
-    while (currFamily && !result) {
-        familyIndex++;
-        if (currFamily->family().length()) {
-            if (fontSelector)
-                result = fontSelector->getFontData(font.fontDescription(), currFamily->family());
-
-            if (!result)
-                result = getCachedFontData(font.fontDescription(), currFamily->family());
-        }
-        currFamily = currFamily->next();
-    }
-
-    if (!currFamily)
-        familyIndex = cAllFamiliesScanned;
-
-    if (!result)
-        // We didn't find a font. Try to find a similar font using our own specific knowledge about our platform.
-        // For example on OS X, we know to map any families containing the words Arabic, Pashto, or Urdu to the
-        // Geeza Pro font.
-        result = getSimilarFontPlatformData(font);
-
-    if (!result && startIndex == 0) {
-        // If it's the primary font that we couldn't find, we try the following. In all other cases, we will
-        // just use per-character system fallback.
-
-        if (fontSelector) {
-            // Try the user's preferred standard font.
-            if (FontData* data = fontSelector->getFontData(font.fontDescription(), standardFamily))
-                return data;
-        }
-
-        // Still no result.  Hand back our last resort fallback font.
-        result = getLastResortFallbackFont(font.fontDescription());
+#if PLATFORM(IOS)
+    FontLocker fontLocker;
+#endif
+    unsigned count = 0;
+    for (auto& font : cachedFonts().values()) {
+        if (font->hasOneRef())
+            ++count;
     }
-    return result;
+    return count;
 }
 
 static HashSet<FontSelector*>* gClients;
@@ -458,9 +511,9 @@ void FontCache::removeClient(FontSelector* client)
     gClients->remove(client);
 }
 
-static unsigned gGeneration = 0;
+static unsigned short gGeneration = 0;
 
-unsigned FontCache::generation()
+unsigned short FontCache::generation()
 {
     return gGeneration;
 }
@@ -468,30 +521,31 @@ unsigned FontCache::generation()
 void FontCache::invalidate()
 {
     if (!gClients) {
-        ASSERT(!gFontPlatformDataCache);
+        ASSERT(fontPlatformDataCache().isEmpty());
         return;
     }
 
-    if (gFontPlatformDataCache) {
-        deleteAllValues(*gFontPlatformDataCache);
-        delete gFontPlatformDataCache;
-        gFontPlatformDataCache = new FontPlatformDataCache;
-    }
+    fontPlatformDataCache().clear();
+    invalidateFontCascadeCache();
 
     gGeneration++;
 
-    Vector<RefPtr<FontSelector> > clients;
-    size_t numClients = gClients->size();
-    clients.reserveInitialCapacity(numClients);
-    HashSet<FontSelector*>::iterator end = gClients->end();
-    for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it)
-        clients.append(*it);
+    Vector<Ref<FontSelector>> clients;
+    clients.reserveInitialCapacity(gClients->size());
+    for (auto it = gClients->begin(), end = gClients->end(); it != end; ++it)
+        clients.uncheckedAppend(**it);
 
-    ASSERT(numClients == clients.size());
-    for (size_t i = 0; i < numClients; ++i)
+    for (unsigned i = 0; i < clients.size(); ++i)
         clients[i]->fontCacheInvalidated();
 
     purgeInactiveFontData();
 }
 
+#if !PLATFORM(COCOA)
+RefPtr<Font> FontCache::similarFont(const FontDescription&)
+{
+    return nullptr;
+}
+#endif
+
 } // namespace WebCore