bug 688125 - part 2 - add memory reporting for fonts in the gfxFontCache. r=njn
authorJonathan Kew <jfkthame@gmail.com>
Fri, 23 Mar 2012 12:14:16 +0000
changeset 91962 7beb8fd466295eb9e0033093abdbaf12ff473fb6
parent 91961 730c2e84247f37939b2c30417009dcd5c17c217c
child 91963 21fabfdbeaab98df15d9980b679b901cb7fb87a9
push idunknown
push userunknown
push dateunknown
reviewersnjn
bugs688125
milestone14.0a1
bug 688125 - part 2 - add memory reporting for fonts in the gfxFontCache. r=njn
gfx/thebes/gfxDWriteFonts.cpp
gfx/thebes/gfxDWriteFonts.h
gfx/thebes/gfxFT2Fonts.cpp
gfx/thebes/gfxFT2Fonts.h
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxGDIFont.cpp
gfx/thebes/gfxGDIFont.h
gfx/thebes/gfxMacFont.cpp
gfx/thebes/gfxMacFont.h
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -757,8 +757,25 @@ gfxDWriteFont::MeasureGlyphWidth(PRUint1
                   GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL,
                   &aGlyph, 1, &metrics, FALSE);
         if (SUCCEEDED(hr)) {
             return NS_lround(metrics.advanceWidth * mFUnitsConvFactor);
         }
     }
     return 0;
 }
+
+void
+gfxDWriteFont::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                   FontCacheSizes*   aSizes) const
+{
+    gfxFont::SizeOfExcludingThis(aMallocSizeOf, aSizes);
+    aSizes->mFontInstances += aMallocSizeOf(mMetrics) +
+        mGlyphWidths.SizeOfExcludingThis(nsnull, aMallocSizeOf);
+}
+
+void
+gfxDWriteFont::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                   FontCacheSizes*   aSizes) const
+{
+    aSizes->mFontInstances += aMallocSizeOf(this);
+    SizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
--- a/gfx/thebes/gfxDWriteFonts.h
+++ b/gfx/thebes/gfxDWriteFonts.h
@@ -87,16 +87,21 @@ public:
     virtual hb_blob_t *GetFontTable(PRUint32 aTag);
 
     virtual bool ProvidesGlyphWidths();
 
     virtual PRInt32 GetGlyphWidth(gfxContext *aCtx, PRUint16 aGID);
 
     virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions> GetGlyphRenderingOptions();
 
+    virtual void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                     FontCacheSizes*   aSizes) const;
+    virtual void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                     FontCacheSizes*   aSizes) const;
+
 protected:
     friend class gfxDWriteShaper;
 
     virtual void CreatePlatformShaper();
 
     bool GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics);
 
     void ComputeMetrics(AntialiasOption anAAOption);
--- a/gfx/thebes/gfxFT2Fonts.cpp
+++ b/gfx/thebes/gfxFT2Fonts.cpp
@@ -656,8 +656,25 @@ gfxFT2Font::FillGlyphDataForChar(PRUint3
         return;
     }
 
     gd->glyphIndex = gid;
     gd->lsbDelta = face->glyph->lsb_delta;
     gd->rsbDelta = face->glyph->rsb_delta;
     gd->xAdvance = face->glyph->advance.x;
 }
+
+void
+gfxFT2Font::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                FontCacheSizes*   aSizes) const
+{
+    gfxFont::SizeOfExcludingThis(aMallocSizeOf, aSizes);
+    aSizes->mFontInstances +=
+        mCharGlyphCache.SizeOfExcludingThis(nsnull, aMallocSizeOf);
+}
+
+void
+gfxFT2Font::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                FontCacheSizes*   aSizes) const
+{
+    aSizes->mFontInstances += aMallocSizeOf(this);
+    SizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
--- a/gfx/thebes/gfxFT2Fonts.h
+++ b/gfx/thebes/gfxFT2Fonts.h
@@ -91,16 +91,21 @@ public: // new functions
         if (entry->mData.glyphIndex == 0xffffffffU) {
             // this is a new entry, fill it
             FillGlyphDataForChar(ch, &entry->mData);
         }
 
         return &entry->mData;
     }
 
+    virtual void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                     FontCacheSizes*   aSizes) const;
+    virtual void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                     FontCacheSizes*   aSizes) const;
+
 protected:
     virtual bool ShapeWord(gfxContext *aContext,
                            gfxShapedWord *aShapedWord,
                            const PRUnichar *aString,
                            bool aPreferPlatformShaping = false);
 
     void FillGlyphDataForChar(PRUint32 ch, CachedGlyphData *gd);
 
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -42,17 +42,16 @@
 #define FORCE_PR_LOG /* Allow logging in the release build */
 #endif
 #include "prlog.h"
 
 #include "nsServiceManagerUtils.h"
 #include "nsReadableUtils.h"
 #include "nsExpirationTracker.h"
 #include "nsILanguageAtomService.h"
-#include "nsIMemoryReporter.h"
 #include "nsITimer.h"
 
 #include "gfxFont.h"
 #include "gfxPlatform.h"
 #include "gfxAtoms.h"
 
 #include "prtypes.h"
 #include "gfxTypes.h"
@@ -1043,16 +1042,62 @@ gfxFontFamily::SizeOfIncludingThis(nsMal
 /*
  * gfxFontCache - global cache of gfxFont instances.
  * Expires unused fonts after a short interval;
  * notifies fonts to age their cached shaped-word records;
  * observes memory-pressure notification and tells fonts to clear their
  * shaped-word caches to free up memory.
  */
 
+NS_IMPL_ISUPPORTS1(gfxFontCache::MemoryReporter, nsIMemoryMultiReporter)
+
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(FontCacheMallocSizeOf, "font-cache")
+
+NS_IMETHODIMP
+gfxFontCache::MemoryReporter::GetName(nsACString &aName)
+{
+    aName.AssignLiteral("font-cache");
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+gfxFontCache::MemoryReporter::CollectReports
+    (nsIMemoryMultiReporterCallback* aCb,
+     nsISupports* aClosure)
+{
+    FontCacheSizes sizes;
+
+    gfxFontCache::GetCache()->SizeOfIncludingThis(&FontCacheMallocSizeOf,
+                                                  &sizes);
+
+    aCb->Callback(EmptyCString(),
+                  NS_LITERAL_CSTRING("explicit/gfx/font-cache"),
+                  nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
+                  sizes.mFontInstances,
+                  NS_LITERAL_CSTRING("Memory used for active font instances."),
+                  aClosure);
+
+    aCb->Callback(EmptyCString(),
+                  NS_LITERAL_CSTRING("explicit/gfx/font-shaped-words"),
+                  nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
+                  sizes.mShapedWords,
+                  NS_LITERAL_CSTRING("Memory used to cache shaped glyph data."),
+                  aClosure);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+gfxFontCache::MemoryReporter::GetExplicitNonHeap(PRInt64* aAmount)
+{
+    // This reporter only measures heap memory.
+    *aAmount = 0;
+    return NS_OK;
+}
+
 // Observer for the memory-pressure notification, to trigger
 // flushing of the shaped-word caches
 class MemoryPressureObserver : public nsIObserver,
                                public nsSupportsWeakReference
 {
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
@@ -1074,17 +1119,21 @@ MemoryPressureObserver::Observe(nsISuppo
     return NS_OK;
 }
 
 nsresult
 gfxFontCache::Init()
 {
     NS_ASSERTION(!gGlobalCache, "Where did this come from?");
     gGlobalCache = new gfxFontCache();
-    return gGlobalCache ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+    if (!gGlobalCache) {
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+    NS_RegisterMemoryMultiReporter(new MemoryReporter);
+    return NS_OK;
 }
 
 void
 gfxFontCache::Shutdown()
 {
     delete gGlobalCache;
     gGlobalCache = nsnull;
 
@@ -1235,16 +1284,49 @@ gfxFontCache::WordCacheExpirationTimerCa
 /*static*/
 PLDHashOperator
 gfxFontCache::ClearCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
 {
     aHashEntry->mFont->ClearCachedWords();
     return PL_DHASH_NEXT;
 }
 
+/*static*/
+size_t
+gfxFontCache::SizeOfFontEntryExcludingThis(HashEntry*        aHashEntry,
+                                           nsMallocSizeOfFun aMallocSizeOf,
+                                           void*             aUserArg)
+{
+    HashEntry *entry = static_cast<HashEntry*>(aHashEntry);
+    FontCacheSizes *sizes = static_cast<FontCacheSizes*>(aUserArg);
+    entry->mFont->SizeOfExcludingThis(aMallocSizeOf, sizes);
+
+    // The font records its size in the |sizes| parameter, so we return zero
+    // here to the hashtable enumerator.
+    return 0;
+}
+
+void
+gfxFontCache::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                  FontCacheSizes*   aSizes) const
+{
+    // TODO: add the overhead of the expiration tracker (generation arrays)
+
+    mFonts.SizeOfExcludingThis(SizeOfFontEntryExcludingThis,
+                               aMallocSizeOf, aSizes);
+}
+
+void
+gfxFontCache::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                  FontCacheSizes*   aSizes) const
+{
+    aSizes->mFontInstances += aMallocSizeOf(this);
+    SizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
 void
 gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
 {
     mAscent = NS_MAX(mAscent, aOther.mAscent);
     mDescent = NS_MAX(mDescent, aOther.mDescent);
     if (aOtherIsOnLeft) {
         mBoundingBox =
             (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
@@ -2584,20 +2666,50 @@ gfxFont::SynthesizeSpaceWidth(PRUint32 a
     case 0x2008: return GetMetrics().spaceWidth; // punctuation space 
     case 0x2009: return GetAdjustedSize() / 5;   // thin space
     case 0x200a: return GetAdjustedSize() / 10;  // hair space
     case 0x202f: return GetAdjustedSize() / 5;   // narrow no-break space
     default: return -1.0;
     }
 }
 
+/*static*/ size_t
+gfxFont::WordCacheEntrySizeOfExcludingThis(CacheHashEntry*   aHashEntry,
+                                           nsMallocSizeOfFun aMallocSizeOf,
+                                           void*             aUserArg)
+{
+    return aMallocSizeOf(aHashEntry->mShapedWord.get());
+}
+
+void
+gfxFont::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                             FontCacheSizes*   aSizes) const
+{
+    for (PRUint32 i = 0; i < mGlyphExtentsArray.Length(); ++i) {
+        aSizes->mFontInstances +=
+            mGlyphExtentsArray[i]->SizeOfIncludingThis(aMallocSizeOf);
+    }
+    aSizes->mShapedWords +=
+        mWordCache.SizeOfExcludingThis(WordCacheEntrySizeOfExcludingThis,
+                                       aMallocSizeOf);
+}
+
+void
+gfxFont::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                             FontCacheSizes*   aSizes) const
+{
+    aSizes->mFontInstances += aMallocSizeOf(this);
+    SizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
+
 gfxGlyphExtents::~gfxGlyphExtents()
 {
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
-    gGlyphExtentsWidthsTotalSize += mContainedGlyphWidths.ComputeSize();
+    gGlyphExtentsWidthsTotalSize +=
+        mContainedGlyphWidths.SizeOfExcludingThis(&FontCacheMallocSizeOf);
     gGlyphExtentsCount++;
 #endif
     MOZ_COUNT_DTOR(gfxGlyphExtents);
 }
 
 bool
 gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
     gfxContext *aContext, PRUint32 aGlyphID, gfxRect *aExtents)
@@ -2631,31 +2743,29 @@ gfxGlyphExtents::GlyphWidths::~GlyphWidt
     for (i = 0; i < mBlocks.Length(); ++i) {
         PtrBits bits = mBlocks[i];
         if (bits && !(bits & 0x1)) {
             delete[] reinterpret_cast<PRUint16 *>(bits);
         }
     }
 }
 
-#ifdef DEBUG
 PRUint32
-gfxGlyphExtents::GlyphWidths::ComputeSize()
+gfxGlyphExtents::GlyphWidths::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
 {
     PRUint32 i;
-    PRUint32 size = mBlocks.Capacity()*sizeof(PtrBits);
+    PRUint32 size = mBlocks.SizeOfExcludingThis(aMallocSizeOf);
     for (i = 0; i < mBlocks.Length(); ++i) {
         PtrBits bits = mBlocks[i];
         if (bits && !(bits & 0x1)) {
-            size += BLOCK_SIZE*sizeof(PRUint16);
+            size += aMallocSizeOf(reinterpret_cast<void*>(bits));
         }
     }
     return size;
 }
-#endif
 
 void
 gfxGlyphExtents::GlyphWidths::Set(PRUint32 aGlyphID, PRUint16 aWidth)
 {
     PRUint32 block = aGlyphID >> BLOCK_SIZE_BITS;
     PRUint32 len = mBlocks.Length();
     if (block >= len) {
         PtrBits *elems = mBlocks.AppendElements(block + 1 - len);
@@ -2697,16 +2807,29 @@ gfxGlyphExtents::SetTightGlyphExtents(PR
     if (!entry)
         return;
     entry->x = aExtentsAppUnits.X();
     entry->y = aExtentsAppUnits.Y();
     entry->width = aExtentsAppUnits.Width();
     entry->height = aExtentsAppUnits.Height();
 }
 
+size_t
+gfxGlyphExtents::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
+{
+    return mContainedGlyphWidths.SizeOfExcludingThis(aMallocSizeOf) +
+        mTightGlyphExtents.SizeOfExcludingThis(nsnull, aMallocSizeOf);
+}
+
+size_t
+gfxGlyphExtents::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
+{
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 gfxFontGroup::gfxFontGroup(const nsAString& aFamilies, const gfxFontStyle *aStyle, gfxUserFontSet *aUserFontSet)
     : mFamilies(aFamilies), mStyle(*aStyle), mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
 {
     mUserFontSet = nsnull;
     SetUserFontSet(aUserFontSet);
 
     mSkipDrawing = false;
 
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -54,16 +54,17 @@
 #include "gfxRect.h"
 #include "nsExpirationTracker.h"
 #include "gfxFontConstants.h"
 #include "gfxPlatform.h"
 #include "nsIAtom.h"
 #include "nsISupportsImpl.h"
 #include "gfxPattern.h"
 #include "mozilla/HashFunctions.h"
+#include "nsIMemoryReporter.h"
 
 typedef struct _cairo_scaled_font cairo_scaled_font_t;
 
 #ifdef DEBUG
 #include <stdio.h>
 #endif
 
 class gfxContext;
@@ -737,16 +738,25 @@ struct gfxTextRange {
  * by the fonts if they get aged three times without being re-used in the
  * meantime.
  *
  * Note that the ShapedWord timeout is much larger than the font timeout,
  * so that in the case of a short-lived font, we'll discard the gfxFont
  * completely, with all its words, and avoid the cost of aging the words
  * individually. That only happens with longer-lived fonts.
  */
+struct FontCacheSizes {
+    FontCacheSizes()
+        : mFontInstances(0), mShapedWords(0)
+    { }
+
+    size_t mFontInstances; // memory used by instances of gfxFont subclasses
+    size_t mShapedWords; // memory used by the per-font shapedWord caches
+};
+
 class THEBES_API gfxFontCache MOZ_FINAL : public nsExpirationTracker<gfxFont,3> {
 public:
     enum {
         FONT_TIMEOUT_SECONDS = 10,
         SHAPED_WORD_TIMEOUT_SECONDS = 60
     };
 
     gfxFontCache();
@@ -790,17 +800,30 @@ public:
         mFonts.Clear();
         AgeAllGenerations();
     }
 
     void FlushShapedWordCaches() {
         mFonts.EnumerateEntries(ClearCachedWordsForFont, nsnull);
     }
 
+    void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                             FontCacheSizes*   aSizes) const;
+    void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                             FontCacheSizes*   aSizes) const;
+
 protected:
+    class MemoryReporter
+        : public nsIMemoryMultiReporter
+    {
+    public:
+        NS_DECL_ISUPPORTS
+        NS_DECL_NSIMEMORYMULTIREPORTER
+    };
+
     void DestroyFont(gfxFont *aFont);
 
     static gfxFontCache *gGlobalCache;
 
     struct Key {
         const gfxFontEntry* mFontEntry;
         const gfxFontStyle* mStyle;
         Key(const gfxFontEntry* aFontEntry, const gfxFontStyle* aStyle)
@@ -823,16 +846,20 @@ protected:
         static PLDHashNumber HashKey(const KeyTypePointer aKey) {
             return mozilla::HashGeneric(aKey->mStyle->Hash(), aKey->mFontEntry);
         }
         enum { ALLOW_MEMMOVE = true };
 
         gfxFont* mFont;
     };
 
+    static size_t SizeOfFontEntryExcludingThis(HashEntry*        aHashEntry,
+                                               nsMallocSizeOfFun aMallocSizeOf,
+                                               void*             aUserArg);
+
     nsTHashtable<HashEntry> mFonts;
 
     static PLDHashOperator ClearCachedWordsForFont(HashEntry* aHashEntry, void*);
     static PLDHashOperator AgeCachedWordsForFont(HashEntry* aHashEntry, void*);
     static void WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache);
     nsCOMPtr<nsITimer>      mWordCacheExpirationTimer;
 };
 
@@ -994,16 +1021,19 @@ public:
 
     void SetContainedGlyphWidthAppUnits(PRUint32 aGlyphID, PRUint16 aWidth) {
         mContainedGlyphWidths.Set(aGlyphID, aWidth);
     }
     void SetTightGlyphExtents(PRUint32 aGlyphID, const gfxRect& aExtentsAppUnits);
 
     PRUint32 GetAppUnitsPerDevUnit() { return mAppUnitsPerDevUnit; }
 
+    size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
+    size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
+
 private:
     class HashEntry : public nsUint32HashKey {
     public:
         // When constructing a new entry in the hashtable, we'll leave this
         // blank. The caller of Put() will fill this in.
         HashEntry(KeyTypePointer aPtr) : nsUint32HashKey(aPtr) {}
         HashEntry(const HashEntry& toCopy) : nsUint32HashKey(toCopy) {
           x = toCopy.x; y = toCopy.y; width = toCopy.width; height = toCopy.height;
@@ -1030,19 +1060,17 @@ private:
                 if (GetGlyphOffset(bits) != indexInBlock)
                     return INVALID_WIDTH;
                 return GetWidth(bits);
             }
             PRUint16 *widths = reinterpret_cast<PRUint16 *>(bits);
             return widths[indexInBlock];
         }
 
-#ifdef DEBUG
-        PRUint32 ComputeSize();
-#endif
+        PRUint32 SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
         
         ~GlyphWidths();
 
     private:
         static PRUint32 GetGlyphOffset(PtrBits aBits) {
             NS_ASSERTION(aBits & 0x1, "This is really a pointer...");
             return (aBits >> 1) & ((1 << BLOCK_SIZE_BITS) - 1);
         }
@@ -1051,17 +1079,17 @@ private:
             return aBits >> (1 + BLOCK_SIZE_BITS);
         }
         static PtrBits MakeSingle(PRUint32 aGlyphOffset, PRUint16 aWidth) {
             return (aWidth << (1 + BLOCK_SIZE_BITS)) + (aGlyphOffset << 1) + 1;
         }
 
         nsTArray<PtrBits> mBlocks;
     };
-    
+
     GlyphWidths             mContainedGlyphWidths;
     nsTHashtable<HashEntry> mTightGlyphExtents;
     PRUint32                mAppUnitsPerDevUnit;
 };
 
 /**
  * gfxFontShaper
  *
@@ -1488,16 +1516,21 @@ public:
 
     // Discard all cached word records; called on memory-pressure notification.
     void ClearCachedWords() {
         if (mWordCache.IsInitialized()) {
             mWordCache.Clear();
         }
     }
 
+    virtual void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                     FontCacheSizes*   aSizes) const;
+    virtual void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                     FontCacheSizes*   aSizes) const;
+
 protected:
     // Call the appropriate shaper to generate glyphs for aText and store
     // them into aShapedWord.
     // The length of the text is aShapedWord->Length().
     virtual bool ShapeWord(gfxContext *aContext,
                            gfxShapedWord *aShapedWord,
                            const PRUnichar *aText,
                            bool aPreferPlatformShaping = false);
@@ -1572,16 +1605,21 @@ protected:
             return aKey->mHashKey;
         }
 
         enum { ALLOW_MEMMOVE = true };
 
         nsAutoPtr<gfxShapedWord> mShapedWord;
     };
 
+    static size_t
+    WordCacheEntrySizeOfExcludingThis(CacheHashEntry*   aHashEntry,
+                                      nsMallocSizeOfFun aMallocSizeOf,
+                                      void*             aUserArg);
+
     nsTHashtable<CacheHashEntry> mWordCache;
 
     static PLDHashOperator AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData);
     static const PRUint32  kShapedWordCacheMaxAge = 3;
 
     bool                       mIsValid;
 
     // use synthetic bolding for environments where this is not supported
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -555,8 +555,25 @@ gfxGDIFont::GetGlyphWidth(gfxContext *aC
         // ensure width is positive, 16.16 fixed-point value
         width = (devWidth & 0x7fff) << 16;
         mGlyphWidths.Put(aGID, width);
         return width;
     }
 
     return -1;
 }
+
+void
+gfxGDIFont::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                FontCacheSizes*   aSizes) const
+{
+    gfxFont::SizeOfExcludingThis(aMallocSizeOf, aSizes);
+    aSizes->mFontInstances += aMallocSizeOf(mMetrics) +
+        mGlyphWidths.SizeOfExcludingThis(nsnull, aMallocSizeOf);
+}
+
+void
+gfxGDIFont::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                FontCacheSizes*   aSizes) const
+{
+    aSizes->mFontInstances += aMallocSizeOf(this);
+    SizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
--- a/gfx/thebes/gfxGDIFont.h
+++ b/gfx/thebes/gfxGDIFont.h
@@ -83,16 +83,21 @@ public:
     /* required for MathML to suppress effects of ClearType "padding" */
     virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption);
 
     virtual bool ProvidesGlyphWidths() { return true; }
 
     // get hinted glyph width in pixels as 16.16 fixed-point value
     virtual PRInt32 GetGlyphWidth(gfxContext *aCtx, PRUint16 aGID);
 
+    virtual void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                     FontCacheSizes*   aSizes) const;
+    virtual void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                     FontCacheSizes*   aSizes) const;
+
 protected:
     virtual void CreatePlatformShaper();
 
     /* override to check for uniscribe failure and fall back to GDI */
     virtual bool ShapeWord(gfxContext *aContext,
                            gfxShapedWord *aShapedWord,
                            const PRUnichar *aString,
                            bool aPreferPlatformShaping = false);
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -504,8 +504,24 @@ gfxMacFont::GetScaledFont()
     nativeFont.mType = NATIVE_FONT_MAC_FONT_FACE;
     nativeFont.mFont = GetCGFontRef();
     mAzureFont = mozilla::gfx::Factory::CreateScaledFontWithCairo(nativeFont, GetAdjustedSize(), mScaledFont);
   }
 
   return mAzureFont;
 }
 
+void
+gfxMacFont::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                FontCacheSizes*   aSizes) const
+{
+    gfxFont::SizeOfExcludingThis(aMallocSizeOf, aSizes);
+    // mCGFont is shared with the font entry, so not counted here;
+    // and we don't have APIs to measure the cairo mFontFace object
+}
+
+void
+gfxMacFont::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                FontCacheSizes*   aSizes) const
+{
+    aSizes->mFontInstances += aMallocSizeOf(this);
+    SizeOfExcludingThis(aMallocSizeOf, aSizes);
+}
--- a/gfx/thebes/gfxMacFont.h
+++ b/gfx/thebes/gfxMacFont.h
@@ -76,16 +76,21 @@ public:
                                Spacing *aSpacing);
 
     // override gfxFont table access function to bypass gfxFontEntry cache,
     // use CGFontRef API to get direct access to system font data
     virtual hb_blob_t *GetFontTable(PRUint32 aTag);
 
     mozilla::RefPtr<mozilla::gfx::ScaledFont> GetScaledFont();
 
+    virtual void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                     FontCacheSizes*   aSizes) const;
+    virtual void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
+                                     FontCacheSizes*   aSizes) const;
+
 protected:
     virtual void CreatePlatformShaper();
 
     // override to prefer CoreText shaping with fonts that depend on AAT
     virtual bool ShapeWord(gfxContext *aContext,
                            gfxShapedWord *aShapedWord,
                            const PRUnichar *aText,
                            bool aPreferPlatformShaping = false);