WebCore:
authormitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 May 2008 21:02:27 +0000 (21:02 +0000)
committermitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 12 May 2008 21:02:27 +0000 (21:02 +0000)
        Reviewed by Ada Chan and Sam Weinig.

        - WebCore changes for https://bugs.webkit.org/show_bug.cgi?id=17097
          <rdar://problem/5715471> CGFontRefs (and HFONTs on Windows) leak because FontCache grows without bound

        Added a way for clients to let the cache know that they no longer need
        font data, which lets the cache release it. Changed clients to track
        most of the font data they get from the cache so that they can later
        release it. Some instances of font data -- namely, those used for system
        font fallback -- are still not tracked and will therefore remain in the
        cache indefinitely.

        * WebCore.base.exp: Added exports for WebCoreStatistics in WebKit.

        * platform/graphics/Font.cpp:
        (WebCore::Font::Font): Changed to use FontFallbackList::create().
        (WebCore::Font::update): Ditto.

        * platform/graphics/FontCache.cpp:
        (WebCore::FontCache::getCachedFontData): Added code to track the number
        of times a SimpleFontData instance has been requested from the cache,
        remove requested instances from the inactive list, and purge inactive
        font data if the inactive list has grown above its maximum allowed size.
        (WebCore::FontCache::releaseFontData): Added. Called by clients to let
        the cache know that they no longer need the font data. Adds the font
        data to the inactive list if the last client has released it.
        (WebCore::FontCache::purgeInactiveFontData): Added. Removes inactive
        font data from the cache (and the inactive list).
        (WebCore::FontCache::fontDataCount): Added to provide statistics.
        (WebCore::FontCache::inactiveFontDataCount): Ditto.

        * platform/graphics/FontCache.h:

        * platform/graphics/FontData.h:
        (WebCore::FontData::FontData): Added a member variable to store the
        highest glyph page tree level in which there is a node for this FontData.
        This is used to limit the depth of the search when pruning glyph page
        trees.
        (WebCore::FontData::setMaxGlyphPageTreeLevel): Added this accessor.
        (WebCore::FontData::maxGlyphPageTreeLevel): Ditto.

        * platform/graphics/FontFallbackList.cpp:
        (WebCore::FontFallbackList::FontFallbackList): Changed to start with a
        refcount of 1.
        (WebCore::FontFallbackList::invalidate): Added a call to
        releaseFontData().
        (WebCore::FontFallbackList::releaseFontData): Added. Lets the font cache
        know that we no longer need the FontData in our font list.
        (WebCore::FontFallbackList::fontDataAt): Changed to record in the font
        list whether the font data is a custom font data or not.
        (WebCore::FontFallbackList::setPlatformFont): Ditto.

        * platform/graphics/FontFallbackList.h:
        (WebCore::FontFallbackList::create): Added and made the constructor
        private.
        (WebCore::FontFallbackList::~FontFallbackList): Added a call to
        releaseFontData().

        * platform/graphics/GlyphPageTreeNode.cpp:
        (WebCore::GlyphPageTreeNode::treeGlyphPageCount): Added to provide
        statistics.
        (WebCore::GlyphPageTreeNode::pageCount): Ditto.

        (WebCore::GlyphPageTreeNode::pruneTreeFontData): Added.
        (WebCore::GlyphPageTreeNode::getChild): Added code to update the font
        data's maximum glyph page tree level.
        (WebCore::GlyphPageTreeNode::pruneFontData): Added.

        * platform/graphics/GlyphPageTreeNode.h:

        * platform/graphics/SimpleFontData.cpp:
        (WebCore::SimpleFontData::~SimpleFontData): Added code to let the font
        cache know that we no longer need the small caps font data and to prune
        the glyph page trees.

WebKit/mac:

        Reviewed by Ada Chan.

        - WebKit/mac changes for https://bugs.webkit.org/show_bug.cgi?id=17097
          <rdar://problem/5715471> CGFontRefs (and HFONTs on Windows) leak because FontCache grows without bound

        Added font cache statistics and a function to purge inactive font data.

        * Misc/WebCoreStatistics.h:
        * Misc/WebCoreStatistics.mm:
        (+[WebCoreStatistics cachedFontDataCount]):
        (+[WebCoreStatistics cachedFontDataInactiveCount]):
        (+[WebCoreStatistics purgeInactiveFontData]):
        (+[WebCoreStatistics glyphPageCount]):

WebKit/win:

        Reviewed by Ada Chan.

        - WebKit/win changes for https://bugs.webkit.org/show_bug.cgi?id=17097
          <rdar://problem/5715471> CGFontRefs (and HFONTs on Windows) leak because FontCache grows without bound

        Added font cache statistics and a function to purge inactive font data.

        * Interfaces/IWebCoreStatistics.idl:
        * WebCoreStatistics.cpp:
        (WebCoreStatistics::cachedFontDataCount):
        (WebCoreStatistics::cachedFontDataInactiveCount):
        (WebCoreStatistics::purgeInactiveFontData):
        (WebCoreStatistics::glyphPageCount):
        * WebCoreStatistics.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@33056 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
WebCore/ChangeLog
WebCore/WebCore.base.exp
WebCore/platform/graphics/Font.cpp
WebCore/platform/graphics/FontCache.cpp
WebCore/platform/graphics/FontCache.h
WebCore/platform/graphics/FontData.h
WebCore/platform/graphics/FontFallbackList.cpp
WebCore/platform/graphics/FontFallbackList.h
WebCore/platform/graphics/GlyphPageTreeNode.cpp
WebCore/platform/graphics/GlyphPageTreeNode.h
WebCore/platform/graphics/SimpleFontData.cpp
WebKit/mac/ChangeLog
WebKit/mac/Misc/WebCoreStatistics.h
WebKit/mac/Misc/WebCoreStatistics.mm
WebKit/win/ChangeLog
WebKit/win/Interfaces/IWebCoreStatistics.idl
WebKit/win/WebCoreStatistics.cpp
WebKit/win/WebCoreStatistics.h

index 753838a..2edcbe8 100644 (file)
@@ -1,3 +1,80 @@
+2008-05-12  Dan Bernstein  <mitz@apple.com>
+
+        Reviewed by Ada Chan and Sam Weinig.
+
+        - WebCore changes for https://bugs.webkit.org/show_bug.cgi?id=17097
+          <rdar://problem/5715471> CGFontRefs (and HFONTs on Windows) leak because FontCache grows without bound
+
+        Added a way for clients to let the cache know that they no longer need
+        font data, which lets the cache release it. Changed clients to track
+        most of the font data they get from the cache so that they can later
+        release it. Some instances of font data -- namely, those used for system
+        font fallback -- are still not tracked and will therefore remain in the
+        cache indefinitely.
+
+        * WebCore.base.exp: Added exports for WebCoreStatistics in WebKit.
+
+        * platform/graphics/Font.cpp:
+        (WebCore::Font::Font): Changed to use FontFallbackList::create().
+        (WebCore::Font::update): Ditto.
+
+        * platform/graphics/FontCache.cpp:
+        (WebCore::FontCache::getCachedFontData): Added code to track the number
+        of times a SimpleFontData instance has been requested from the cache,
+        remove requested instances from the inactive list, and purge inactive
+        font data if the inactive list has grown above its maximum allowed size.
+        (WebCore::FontCache::releaseFontData): Added. Called by clients to let
+        the cache know that they no longer need the font data. Adds the font
+        data to the inactive list if the last client has released it.
+        (WebCore::FontCache::purgeInactiveFontData): Added. Removes inactive
+        font data from the cache (and the inactive list).
+        (WebCore::FontCache::fontDataCount): Added to provide statistics.
+        (WebCore::FontCache::inactiveFontDataCount): Ditto.
+
+        * platform/graphics/FontCache.h:
+
+        * platform/graphics/FontData.h:
+        (WebCore::FontData::FontData): Added a member variable to store the
+        highest glyph page tree level in which there is a node for this FontData.
+        This is used to limit the depth of the search when pruning glyph page
+        trees.
+        (WebCore::FontData::setMaxGlyphPageTreeLevel): Added this accessor.
+        (WebCore::FontData::maxGlyphPageTreeLevel): Ditto.
+
+        * platform/graphics/FontFallbackList.cpp:
+        (WebCore::FontFallbackList::FontFallbackList): Changed to start with a
+        refcount of 1.
+        (WebCore::FontFallbackList::invalidate): Added a call to
+        releaseFontData().
+        (WebCore::FontFallbackList::releaseFontData): Added. Lets the font cache
+        know that we no longer need the FontData in our font list.
+        (WebCore::FontFallbackList::fontDataAt): Changed to record in the font
+        list whether the font data is a custom font data or not.
+        (WebCore::FontFallbackList::setPlatformFont): Ditto.
+
+        * platform/graphics/FontFallbackList.h:
+        (WebCore::FontFallbackList::create): Added and made the constructor
+        private.
+        (WebCore::FontFallbackList::~FontFallbackList): Added a call to
+        releaseFontData().
+
+        * platform/graphics/GlyphPageTreeNode.cpp:
+        (WebCore::GlyphPageTreeNode::treeGlyphPageCount): Added to provide
+        statistics.
+        (WebCore::GlyphPageTreeNode::pageCount): Ditto.
+
+        (WebCore::GlyphPageTreeNode::pruneTreeFontData): Added.
+        (WebCore::GlyphPageTreeNode::getChild): Added code to update the font
+        data's maximum glyph page tree level.
+        (WebCore::GlyphPageTreeNode::pruneFontData): Added.
+
+        * platform/graphics/GlyphPageTreeNode.h:
+
+        * platform/graphics/SimpleFontData.cpp:
+        (WebCore::SimpleFontData::~SimpleFontData): Added code to let the font
+        cache know that we no longer need the small caps font data and to prune
+        the glyph page trees.
+
 2008-05-12  Anders Carlsson  <andersca@apple.com>
 
         Reviewed by Alexey.
index 9751944..6ed9f82 100644 (file)
@@ -360,6 +360,7 @@ __ZN7WebCore16createFullMarkupEPKNS_5RangeE
 __ZN7WebCore16enclosingIntRectERK7_NSRect
 __ZN7WebCore16isEndOfParagraphERKNS_15VisiblePositionE
 __ZN7WebCore17DOMImplementation14isTextMIMETypeERKNS_6StringE
+__ZN7WebCore17GlyphPageTreeNode18treeGlyphPageCountEv
 __ZN7WebCore17equalIgnoringCaseEPNS_10StringImplES1_
 __ZN7WebCore18PlatformMouseEventC1EP7NSEvent
 __ZN7WebCore18isStartOfParagraphERKNS_15VisiblePositionE
@@ -578,6 +579,9 @@ __ZN7WebCore8Settings41setNeedsKeyboardEventDisambiguationQuirksEb
 __ZN7WebCore8blankURLEv
 __ZN7WebCore8parseURLERKNS_6StringE
 __ZN7WebCore9FloatRectC1ERK7_NSRect
+__ZN7WebCore9FontCache13fontDataCountEv
+__ZN7WebCore9FontCache21inactiveFontDataCountEv
+__ZN7WebCore9FontCache21purgeInactiveFontDataEi
 __ZN7WebCore9FrameTree11appendChildEN3WTF10PassRefPtrINS_5FrameEEE
 __ZN7WebCore9FrameTree7setNameERKNS_12AtomicStringE
 __ZN7WebCore9FrameView12setMediaTypeERKNS_6StringE
index f10cc8a..f1b7e1c 100644 (file)
@@ -293,7 +293,7 @@ Font::Font(const FontDescription& fd, short letterSpacing, short wordSpacing)
 }
 
 Font::Font(const FontPlatformData& fontData, bool isPrinterFont)
-    : m_fontList(new FontFallbackList)
+    : m_fontList(FontFallbackList::create())
     , m_pageZero(0)
     , m_cachedPrimaryFont(0)
     , m_letterSpacing(0)
@@ -500,7 +500,7 @@ void Font::update(PassRefPtr<FontSelector> fontSelector) const
     // won't stick around long enough to get you in trouble). Still, this is pretty disgusting,
     // and could eventually be rectified by using RefPtrs for Fonts themselves.
     if (!m_fontList)
-        m_fontList = new FontFallbackList();
+        m_fontList = FontFallbackList::create();
     m_fontList->invalidate(fontSelector);
     m_cachedPrimaryFont = 0;
     m_pageZero = 0;
index f7d5995..d91fb89 100644 (file)
@@ -36,6 +36,7 @@
 #include "FontSelector.h"
 #include "StringHash.h"
 #include <wtf/HashMap.h>
+#include <wtf/ListHashSet.h>
 
 using namespace WTF;
 
@@ -214,25 +215,87 @@ struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> {
     }
 };
 
-typedef HashMap<FontPlatformData, SimpleFontData*, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
+typedef HashMap<FontPlatformData, pair<SimpleFontData*, unsigned>, FontDataCacheKeyHash, FontDataCacheKeyTraits> FontDataCache;
 
 static FontDataCache* gFontDataCache = 0;
 
+const int cMaxInactiveFontData = 120;  // Pretty Low Threshold
+const float cInactiveFontDataPurgeRatio = 0.2f;
+static ListHashSet<const SimpleFontData*>* gInactiveFontData = 0;
+
 SimpleFontData* FontCache::getCachedFontData(const FontPlatformData* platformData)
 {
     if (!platformData)
         return 0;
 
-    if (!gFontDataCache)
+    if (!gFontDataCache) {
         gFontDataCache = new FontDataCache;
+        gInactiveFontData = new ListHashSet<const SimpleFontData*>;
+    }
     
-    SimpleFontData* result = gFontDataCache->get(*platformData);
-    if (!result) {
-        result = new SimpleFontData(*platformData);
-        gFontDataCache->set(*platformData, result);
+    FontDataCache::iterator result = gFontDataCache->find(*platformData);
+    if (result == gFontDataCache->end()) {
+        if (gInactiveFontData->size() > cMaxInactiveFontData)
+            purgeInactiveFontData(cMaxInactiveFontData * cInactiveFontDataPurgeRatio);
+
+        pair<SimpleFontData*, unsigned> newValue(new SimpleFontData(*platformData), 1);
+        gFontDataCache->set(*platformData, newValue);
+        return newValue.first;
     }
-        
-    return result;
+    if (!result.get()->second.second++) {
+        ASSERT(gInactiveFontData->contains(result.get()->second.first));
+        gInactiveFontData->remove(result.get()->second.first);
+    }
+
+    return result.get()->second.first;
+}
+
+void FontCache::releaseFontData(const SimpleFontData* fontData)
+{
+    ASSERT(gFontDataCache);
+    ASSERT(!fontData->isCustomFont());
+
+    FontDataCache::iterator it = gFontDataCache->find(static_cast<const SimpleFontData*>(fontData)->platformData());
+    ASSERT(it != gFontDataCache->end());
+
+    if (!--it->second.second)
+        gInactiveFontData->add(static_cast<const SimpleFontData*>(fontData));
+}
+
+void FontCache::purgeInactiveFontData(int count)
+{
+    if (!gInactiveFontData)
+        return;
+
+    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());
+        delete fontData;
+    }
+
+    if (it == end) {
+        // Removed everything
+        gInactiveFontData->clear();
+    } else {
+        for (int i = 0; i < count; ++i)
+            gInactiveFontData->remove(gInactiveFontData->begin());
+    }
+}
+
+size_t FontCache::fontDataCount()
+{
+    if (gFontDataCache)
+        return gFontDataCache->size();
+    return 0;
+}
+
+size_t FontCache::inactiveFontDataCount()
+{
+    if (gInactiveFontData)
+        return gInactiveFontData->size();
+    return 0;
 }
 
 const FontData* FontCache::getFontData(const Font& font, int& familyIndex, FontSelector* fontSelector)
index e1a704b..9026d4d 100644 (file)
@@ -50,8 +50,10 @@ class SimpleFontData;
 class FontCache {
 public:
     static const FontData* getFontData(const Font&, int& familyIndex, FontSelector*);
+    static void releaseFontData(const SimpleFontData*);
     
     // This method is implemented by the platform.
+    // FIXME: Font data returned by this method never go inactive because callers don't track and release them.
     static const SimpleFontData* getFontDataForCharacters(const Font&, const UChar* characters, int length);
     
     // Also implemented by the platform.
@@ -66,7 +68,11 @@ public:
     static FontPlatformData* getCachedFontPlatformData(const FontDescription&, const AtomicString& family, bool checkingAlternateName = false);
     static SimpleFontData* getCachedFontData(const FontPlatformData*);
     static FontPlatformData* getLastResortFallbackFont(const FontDescription&);
-    
+
+    static size_t fontDataCount();
+    static size_t inactiveFontDataCount();
+    static void purgeInactiveFontData(int count = INT_MAX);
+
 private:
     // These methods are implemented by each platform.
     static FontPlatformData* getSimilarFontPlatformData(const Font&);
index 352d965..cb79919 100644 (file)
@@ -35,6 +35,11 @@ class SimpleFontData;
 
 class FontData : Noncopyable {
 public:
+    FontData()
+        : m_maxGlyphPageTreeLevel(0)
+    {
+    }
+
     virtual ~FontData();
 
     virtual const SimpleFontData* fontDataForCharacter(UChar32) const = 0;
@@ -42,6 +47,12 @@ public:
     virtual bool isCustomFont() const = 0;
     virtual bool isLoading() const = 0;
     virtual bool isSegmented() const = 0;
+
+    void setMaxGlyphPageTreeLevel(unsigned level) const { m_maxGlyphPageTreeLevel = level; }
+    unsigned maxGlyphPageTreeLevel() const { return m_maxGlyphPageTreeLevel; }
+
+private:
+    mutable unsigned m_maxGlyphPageTreeLevel;
 };
 
 } // namespace WebCore
index 0fdd99c..2a0db90 100644 (file)
@@ -36,8 +36,7 @@
 namespace WebCore {
 
 FontFallbackList::FontFallbackList()
-    : RefCounted<FontFallbackList>(0)
-    , m_familyIndex(0)
+    : m_familyIndex(0)
     , m_pitch(UnknownPitch)
     , m_loadingCustomFonts(false)
     , m_fontSelector(0)
@@ -46,6 +45,7 @@ FontFallbackList::FontFallbackList()
 
 void FontFallbackList::invalidate(PassRefPtr<FontSelector> fontSelector)
 {
+    releaseFontData();
     m_fontList.clear();
     m_familyIndex = 0;    
     m_pitch = UnknownPitch;
@@ -53,6 +53,17 @@ void FontFallbackList::invalidate(PassRefPtr<FontSelector> fontSelector)
     m_fontSelector = fontSelector;
 }
 
+void FontFallbackList::releaseFontData()
+{
+    unsigned numFonts = m_fontList.size();
+    for (unsigned i = 0; i < numFonts; ++i) {
+        if (!m_fontList[i].second) {
+            ASSERT(!m_fontList[i].first->isSegmented());
+            FontCache::releaseFontData(static_cast<const SimpleFontData*>(m_fontList[i].first));
+        }
+    }
+}
+
 void FontFallbackList::determinePitch(const Font* font) const
 {
     const FontData* fontData = primaryFont(font);
@@ -71,7 +82,7 @@ void FontFallbackList::determinePitch(const Font* font) const
 const FontData* FontFallbackList::fontDataAt(const Font* font, unsigned realizedFontIndex) const
 {
     if (realizedFontIndex < m_fontList.size())
-        return m_fontList[realizedFontIndex]; // This fallback font is already in our list.
+        return m_fontList[realizedFontIndex].first; // This fallback font is already in our list.
 
     // Make sure we're not passing in some crazy value here.
     ASSERT(realizedFontIndex == m_fontList.size());
@@ -85,7 +96,7 @@ const FontData* FontFallbackList::fontDataAt(const Font* font, unsigned realized
     // |m_familyIndex| as it scans for the right font to make.
     const FontData* result = FontCache::getFontData(*font, m_familyIndex, m_fontSelector.get());
     if (result) {
-        m_fontList.append(result);
+        m_fontList.append(pair<const FontData*, bool>(result, result->isCustomFont()));
         if (result->isLoading())
             m_loadingCustomFonts = true;
     }
@@ -110,7 +121,8 @@ const FontData* FontFallbackList::fontDataForCharacters(const Font* font, const
 void FontFallbackList::setPlatformFont(const FontPlatformData& platformData)
 {
     m_familyIndex = cAllFamiliesScanned;
-    m_fontList.append(FontCache::getCachedFontData(&platformData));
+    const FontData* fontData = FontCache::getCachedFontData(&platformData);
+    m_fontList.append(pair<const FontData*, bool>(fontData, fontData->isCustomFont()));
 }
 
 }
index 38128a2..27db9a5 100644 (file)
@@ -41,8 +41,9 @@ const int cAllFamiliesScanned = -1;
 
 class FontFallbackList : public RefCounted<FontFallbackList> {
 public:
-    FontFallbackList();
+    static PassRefPtr<FontFallbackList> create() { return adoptRef(new FontFallbackList()); }
 
+    ~FontFallbackList() { releaseFontData(); }
     void invalidate(PassRefPtr<FontSelector>);
     
     bool isFixedPitch(const Font* f) const { if (m_pitch == UnknownPitch) determinePitch(f); return m_pitch == FixedPitch; };
@@ -53,13 +54,17 @@ public:
     FontSelector* fontSelector() const { return m_fontSelector.get(); }
 
 private:
+    FontFallbackList();
+
     const FontData* primaryFont(const Font* f) const { return fontDataAt(f, 0); }
     const FontData* fontDataAt(const Font*, unsigned index) const;
     const FontData* fontDataForCharacters(const Font*, const UChar*, int length) const;
     
     void setPlatformFont(const FontPlatformData&);
 
-    mutable Vector<const FontData*, 1> m_fontList;
+    void releaseFontData();
+
+    mutable Vector<pair<const FontData*, bool>, 1> m_fontList;
     mutable int m_familyIndex;
     mutable Pitch m_pitch;
     mutable bool m_loadingCustomFonts;
index 53c94b8..45713a1 100644 (file)
@@ -65,6 +65,31 @@ GlyphPageTreeNode* GlyphPageTreeNode::getRoot(unsigned pageNumber)
     return node;
 }
 
+size_t GlyphPageTreeNode::treeGlyphPageCount()
+{
+    size_t count = 0;
+    if (roots) {
+        HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end();
+        for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); it != end; ++it)
+            count += it->second->pageCount();
+    }
+    
+    if (pageZeroRoot)
+        count += pageZeroRoot->pageCount();
+
+    return count;
+}
+
+size_t GlyphPageTreeNode::pageCount() const
+{
+    size_t count = m_page && m_page->owner() == this ? 1 : 0;
+    HashMap<const FontData*, GlyphPageTreeNode*>::const_iterator end = m_children.end();
+    for (HashMap<const FontData*, GlyphPageTreeNode*>::const_iterator it = m_children.begin(); it != end; ++it)
+        count += it->second->pageCount();
+
+    return count;
+}
+
 void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData* fontData)
 {
     // Enumerate all the roots and prune any tree that contains our custom font data.
@@ -78,6 +103,18 @@ void GlyphPageTreeNode::pruneTreeCustomFontData(const FontData* fontData)
         pageZeroRoot->pruneCustomFontData(fontData);
 }
 
+void GlyphPageTreeNode::pruneTreeFontData(const SimpleFontData* fontData)
+{
+    if (roots) {
+        HashMap<int, GlyphPageTreeNode*>::iterator end = roots->end();
+        for (HashMap<int, GlyphPageTreeNode*>::iterator it = roots->begin(); it != end; ++it)
+            it->second->pruneFontData(fontData);
+    }
+    
+    if (pageZeroRoot)
+        pageZeroRoot->pruneFontData(fontData);
+}
+
 GlyphPageTreeNode::~GlyphPageTreeNode()
 {
     deleteAllValues(m_children);
@@ -264,9 +301,10 @@ GlyphPageTreeNode* GlyphPageTreeNode::getChild(const FontData* fontData, unsigne
 #ifndef NDEBUG
         child->m_pageNumber = m_pageNumber;
 #endif
-        if (fontData)
+        if (fontData) {
             m_children.set(fontData, child);
-        else {
+            fontData->setMaxGlyphPageTreeLevel(max(fontData->maxGlyphPageTreeLevel(), child->m_level));
+        } else {
             m_systemFallbackChild = child;
             child->m_isSystemFallback = true;
         }
@@ -298,4 +336,36 @@ void GlyphPageTreeNode::pruneCustomFontData(const FontData* fontData)
         it->second->pruneCustomFontData(fontData);
 }
 
+void GlyphPageTreeNode::pruneFontData(const SimpleFontData* fontData, unsigned level)
+{
+    ASSERT(fontData);
+    if (!fontData)
+        return;
+
+    // Prune any branch that contains this FontData.
+    HashMap<const FontData*, GlyphPageTreeNode*>::iterator child = m_children.find(fontData);
+    if (child == m_children.end()) {
+        // If there is no level-1 node for fontData, then there is no deeper node for it in this tree.
+        if (!level)
+            return;
+    } else {
+        GlyphPageTreeNode* node = child->second;
+        m_children.remove(fontData);
+        unsigned customFontCount = node->m_customFontCount;
+        delete node;
+        if (customFontCount) {
+            for (GlyphPageTreeNode* curr = this; curr; curr = curr->m_parent)
+                curr->m_customFontCount -= customFontCount;
+        }
+    }
+
+    level++;
+    if (level > fontData->maxGlyphPageTreeLevel())
+        return;
+
+    HashMap<const FontData*, GlyphPageTreeNode*>::iterator end = m_children.end();
+    for (HashMap<const FontData*, GlyphPageTreeNode*>::iterator it = m_children.begin(); it != end; ++it)
+        it->second->pruneFontData(fontData, level);
+}
+
 }
index 40b8154..a618b70 100644 (file)
@@ -134,8 +134,10 @@ public:
     }
 
     static void pruneTreeCustomFontData(const FontData*);
+    static void pruneTreeFontData(const SimpleFontData*);
 
     void pruneCustomFontData(const FontData*);
+    void pruneFontData(const SimpleFontData*, unsigned level = 0);
 
     GlyphPageTreeNode* parent() const { return m_parent; }
     GlyphPageTreeNode* getChild(const FontData*, unsigned pageNumber);
@@ -149,6 +151,9 @@ public:
     // The system fallback font has special rules (see above).
     bool isSystemFallback() const { return m_isSystemFallback; }
 
+    static size_t treeGlyphPageCount();
+    size_t pageCount() const;
+
 private:
     static GlyphPageTreeNode* getRoot(unsigned pageNumber);
     void initializePage(const FontData*, unsigned pageNumber);
index 24812f7..372fcc8 100644 (file)
@@ -30,6 +30,7 @@
 #include "config.h"
 #include "SimpleFontData.h"
 
+#include "FontCache.h"
 #if ENABLE(SVG_FONTS)
 #include "SVGFontData.h"
 #include "SVGFontFaceElement.h"
@@ -115,13 +116,16 @@ SimpleFontData::SimpleFontData(const FontPlatformData& f, bool customFont, bool
 
 SimpleFontData::~SimpleFontData()
 {
+    if (!isCustomFont()) {
+        if (m_smallCapsFontData)
+            FontCache::releaseFontData(m_smallCapsFontData);
+        GlyphPageTreeNode::pruneTreeFontData(this);
+    }
+
 #if ENABLE(SVG_FONTS) && !PLATFORM(QT)
     if (!m_svgFontData || !m_svgFontData->svgFontFaceElement())
 #endif
         platformDestroy();
-
-    // We only get deleted when the cache gets cleared.  Since the smallCapsRenderer is also in that cache,
-    // it will be deleted then, so we don't need to do anything here.
 }
 
 float SimpleFontData::widthForGlyph(Glyph glyph) const
index 97f2949..7571081 100644 (file)
@@ -1,3 +1,19 @@
+2008-05-12  Dan Bernstein  <mitz@apple.com>
+
+        Reviewed by Ada Chan.
+
+        - WebKit/mac changes for https://bugs.webkit.org/show_bug.cgi?id=17097
+          <rdar://problem/5715471> CGFontRefs (and HFONTs on Windows) leak because FontCache grows without bound
+
+        Added font cache statistics and a function to purge inactive font data.
+
+        * Misc/WebCoreStatistics.h:
+        * Misc/WebCoreStatistics.mm:
+        (+[WebCoreStatistics cachedFontDataCount]):
+        (+[WebCoreStatistics cachedFontDataInactiveCount]):
+        (+[WebCoreStatistics purgeInactiveFontData]):
+        (+[WebCoreStatistics glyphPageCount]):
+
 2008-05-12  Mark Rowe  <mrowe@apple.com>
 
         Reviewed by Tim Hatcher.
index 7585ec0..392f31b 100644 (file)
 + (size_t)iconRecordCount;
 + (size_t)iconsWithDataCount;
 
++ (size_t)cachedFontDataCount;
++ (size_t)cachedFontDataInactiveCount;
++ (void)purgeInactiveFontData;
++ (size_t)glyphPageCount;
+
 + (BOOL)shouldPrintExceptions;
 + (void)setShouldPrintExceptions:(BOOL)print;
 
index 090b2c9..0482fdb 100644 (file)
 #import "WebCache.h"
 #import "WebFrameInternal.h"
 #import <JavaScriptCore/interpreter.h>
+#import <WebCore/FontCache.h>
 #import <WebCore/Frame.h>
 #import <WebCore/GCController.h>
+#import <WebCore/GlyphPageTreeNode.h>
 #import <WebCore/IconDatabase.h>
 #import <WebCore/RenderTreeAsText.h>
 #import <WebCore/RenderView.h>
@@ -116,6 +118,26 @@ using namespace WebCore;
     return iconDatabase()->iconRecordCountWithData();
 }
 
++ (size_t)cachedFontDataCount
+{
+    return FontCache::fontDataCount();
+}
+
++ (size_t)cachedFontDataInactiveCount
+{
+    return FontCache::inactiveFontDataCount();
+}
+
++ (void)purgeInactiveFontData
+{
+    FontCache::purgeInactiveFontData();
+}
+
++ (size_t)glyphPageCount
+{
+    return GlyphPageTreeNode::treeGlyphPageCount();
+}
+
 + (BOOL)shouldPrintExceptions
 {
     JSLock lock;
index 97c22d9..b9c4042 100644 (file)
@@ -1,3 +1,20 @@
+2008-05-12  Dan Bernstein  <mitz@apple.com>
+
+        Reviewed by Ada Chan.
+
+        - WebKit/win changes for https://bugs.webkit.org/show_bug.cgi?id=17097
+          <rdar://problem/5715471> CGFontRefs (and HFONTs on Windows) leak because FontCache grows without bound
+
+        Added font cache statistics and a function to purge inactive font data.
+
+        * Interfaces/IWebCoreStatistics.idl:
+        * WebCoreStatistics.cpp:
+        (WebCoreStatistics::cachedFontDataCount):
+        (WebCoreStatistics::cachedFontDataInactiveCount):
+        (WebCoreStatistics::purgeInactiveFontData):
+        (WebCoreStatistics::glyphPageCount):
+        * WebCoreStatistics.h:
+
 2008-05-12  Adam Roben  <aroben@apple.com>
 
         Build fix
index 36cd620..dd49639 100644 (file)
@@ -46,4 +46,9 @@ interface IWebCoreStatistics : IUnknown
     HRESULT iconRetainedPageURLCount([out, retval] UINT* count);
     HRESULT iconRecordCount([out, retval] UINT* count);
     HRESULT iconsWithDataCount([out, retval] UINT* count);
+
+    HRESULT cachedFontDataCount([out, retval] UINT* count);
+    HRESULT cachedFontDataInactiveCount([out, retval] UINT* count);
+    HRESULT purgeInactiveFontData();
+    HRESULT glyphPageCount([out, retval] UINT* count);
 }
index 911b76f..bd19a33 100644 (file)
@@ -27,6 +27,8 @@
 #include "WebKitDLL.h"
 #include "WebCoreStatistics.h"
 
+#include <WebCore/FontCache.h>
+#include <WebCore/GlyphPageTreeNode.h>
 #include <WebCore/IconDatabase.h>
 
 #pragma warning(push, 0)
@@ -167,3 +169,36 @@ HRESULT STDMETHODCALLTYPE WebCoreStatistics::iconsWithDataCount(
     *count = (UINT) iconDatabase()->iconRecordCountWithData();
     return S_OK;
 }
+
+HRESULT STDMETHODCALLTYPE WebCoreStatistics::cachedFontDataCount( 
+    /* [retval][out] */ UINT *count)
+{
+    if (!count)
+        return E_POINTER;
+    *count = (UINT) FontCache::fontDataCount();
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebCoreStatistics::cachedFontDataInactiveCount( 
+    /* [retval][out] */ UINT *count)
+{
+    if (!count)
+        return E_POINTER;
+    *count = (UINT) FontCache::inactiveFontDataCount();
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebCoreStatistics::purgeInactiveFontData(void)
+{
+    FontCache::purgeInactiveFontData();
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE WebCoreStatistics::glyphPageCount( 
+    /* [retval][out] */ UINT *count)
+{
+    if (!count)
+        return E_POINTER;
+    *count = (UINT) GlyphPageTreeNode::treeGlyphPageCount();
+    return S_OK;
+}
index 6caa7e5..df6075c 100644 (file)
@@ -58,6 +58,13 @@ public:
         /* [retval][out] */ UINT *count);
     virtual HRESULT STDMETHODCALLTYPE iconsWithDataCount( 
         /* [retval][out] */ UINT *count);
+    virtual HRESULT STDMETHODCALLTYPE cachedFontDataCount( 
+        /* [retval][out] */ UINT *count);
+    virtual HRESULT STDMETHODCALLTYPE cachedFontDataInactiveCount( 
+        /* [retval][out] */ UINT *count);
+    virtual HRESULT STDMETHODCALLTYPE purgeInactiveFontData(void);
+    virtual HRESULT STDMETHODCALLTYPE glyphPageCount( 
+        /* [retval][out] */ UINT *count);
 
 protected:
     ULONG m_refCount;