Bug 710727. Share cmaps across all fonts. r=jkew
authorJohn Daggett <jdaggett@mozilla.com>
Mon, 09 Apr 2012 13:31:55 +0900
changeset 94550 2b364a0ca1da908db47d92dba3edf378f6ecc8a2
parent 94536 5b3f94379f5cd2a1e9871a95e50ba4d1b5af6fcb
child 94551 e0c118cd4f86cde23b457ae9d2b7b25b979bbb6d
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjkew
bugs710727
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 710727. Share cmaps across all fonts. r=jkew
gfx/thebes/gfxDWriteFontList.cpp
gfx/thebes/gfxFT2FontList.cpp
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxFontUtils.h
gfx/thebes/gfxGDIFontList.cpp
gfx/thebes/gfxGDIFontList.h
gfx/thebes/gfxMacPlatformFontList.mm
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxPlatformFontList.h
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -370,94 +370,88 @@ gfxDWriteFontEntry::GetFontTable(PRUint3
 
 nsresult
 gfxDWriteFontEntry::ReadCMAP()
 {
     HRESULT hr;
     nsresult rv;
 
     // attempt this once, if errors occur leave a blank cmap
-    if (mCmapInitialized)
+    if (mCharacterMap) {
         return NS_OK;
-    mCmapInitialized = true;
+    }
+
+    nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
 
     // if loading via GDI, just use GetFontTable
     if (mFont && gfxDWriteFontList::PlatformFontList()->UseGDIFontTableAccess()) {
-        const PRUint32 kCmapTag = TRUETYPE_TAG('c','m','a','p');
-        AutoFallibleTArray<PRUint8,16384> buffer;
+        PRUint32 kCMAP = TRUETYPE_TAG('c','m','a','p');
+        nsresult rv;
+
+        AutoFallibleTArray<PRUint8,16384> cmap;
+        rv = GetFontTable(kCMAP, cmap);
+
+        bool unicodeFont = false, symbolFont = false; // currently ignored
 
-        if (GetFontTable(kCmapTag, buffer) != NS_OK)
-            return NS_ERROR_FAILURE;
-        PRUint8 *cmap = buffer.Elements();
+        if (NS_SUCCEEDED(rv)) {
+            rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
+                                        *charmap, mUVSOffset,
+                                        unicodeFont, symbolFont);
+        }
+    } else {
+        // loading using dwrite, don't use GetFontTable to avoid copy
+        nsRefPtr<IDWriteFontFace> fontFace;
+        rv = CreateFontFace(getter_AddRefs(fontFace));
 
-        bool          unicodeFont = false, symbolFont = false;
-        rv = gfxFontUtils::ReadCMAP(cmap, buffer.Length(),
-                                    mCharacterMap, mUVSOffset,
-                                    unicodeFont, symbolFont);
-#ifdef PR_LOGGING
-        LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n",
-                      NS_ConvertUTF16toUTF8(mName).get(),
-                      mCharacterMap.SizeOfExcludingThis(moz_malloc_size_of)));
-        if (LOG_CMAPDATA_ENABLED()) {
-            char prefix[256];
-            sprintf(prefix, "(cmapdata) name: %.220s",
-                    NS_ConvertUTF16toUTF8(mName).get());
-            mCharacterMap.Dump(prefix, eGfxLog_cmapdata);
+        if (NS_SUCCEEDED(rv)) {
+            const PRUint32 kCmapTag = DWRITE_MAKE_OPENTYPE_TAG('c', 'm', 'a', 'p');
+            PRUint8 *tableData;
+            PRUint32 len;
+            void *tableContext = NULL;
+            BOOL exists;
+            hr = fontFace->TryGetFontTable(kCmapTag, (const void**)&tableData,
+                                           &len, &tableContext, &exists);
+
+            if (SUCCEEDED(hr)) {
+                bool isSymbol = fontFace->IsSymbolFont();
+                bool isUnicode = true;
+                if (exists) {
+                    rv = gfxFontUtils::ReadCMAP(tableData, len, *charmap,
+                                                mUVSOffset, isUnicode,
+                                                isSymbol);
+                }
+                fontFace->ReleaseFontTable(tableContext);
+            } else {
+                rv = NS_ERROR_FAILURE;
+            }
         }
-#endif
-        mHasCmapTable = NS_SUCCEEDED(rv);
-        return rv;
     }
 
-    // loading using dwrite, don't use GetFontTable to avoid copy
-    nsRefPtr<IDWriteFontFace> fontFace;
-    rv = CreateFontFace(getter_AddRefs(fontFace));
-
-    if (NS_FAILED(rv)) {
-        return rv;
+    mHasCmapTable = NS_SUCCEEDED(rv);
+    if (mHasCmapTable) {
+        gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+        mCharacterMap = pfl->FindCharMap(charmap);
+    } else {
+        // if error occurred, initialize to null cmap
+        mCharacterMap = new gfxCharacterMap();
     }
 
-    PRUint8 *tableData;
-    PRUint32 len;
-    void *tableContext = NULL;
-    BOOL exists;
-    hr = fontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('c', 'm', 'a', 'p'),
-                                   (const void**)&tableData,
-                                   &len,
-                                   &tableContext,
-                                   &exists);
-    if (FAILED(hr)) {
-        return NS_ERROR_FAILURE;
-    }
-
-    bool isSymbol = fontFace->IsSymbolFont();
-    bool isUnicode = true;
-    if (exists) {
-        rv = gfxFontUtils::ReadCMAP(tableData,
-                                    len,
-                                    mCharacterMap,
-                                    mUVSOffset,
-                                    isUnicode,
-                                    isSymbol);
-    }
-    fontFace->ReleaseFontTable(tableContext);
-
 #ifdef PR_LOGGING
-    LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n",
+    LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
                   NS_ConvertUTF16toUTF8(mName).get(),
-                  mCharacterMap.SizeOfExcludingThis(moz_malloc_size_of)));
+                  charmap->SizeOfIncludingThis(moz_malloc_size_of),
+                  charmap->mHash, mCharacterMap == charmap ? " new" : ""));
     if (LOG_CMAPDATA_ENABLED()) {
         char prefix[256];
         sprintf(prefix, "(cmapdata) name: %.220s",
                 NS_ConvertUTF16toUTF8(mName).get());
-        mCharacterMap.Dump(prefix, eGfxLog_cmapdata);
+        charmap->Dump(prefix, eGfxLog_cmapdata);
     }
 #endif
 
-    mHasCmapTable = NS_SUCCEEDED(rv);
     return rv;
 }
 
 gfxFont *
 gfxDWriteFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle,
                                        bool aNeedsBold)
 {
     return new gfxDWriteFont(this, aFontStyle, aNeedsBold);
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -338,35 +338,41 @@ FT2FontEntry::CairoFontFace()
                                       userFontData, FTFontDestroyFunc);
     }
     return mFontFace;
 }
 
 nsresult
 FT2FontEntry::ReadCMAP()
 {
-    if (mCmapInitialized) {
+    if (mCharacterMap) {
         return NS_OK;
     }
 
-    // attempt this once, if errors occur leave a blank cmap
-    mCmapInitialized = true;
+    nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
 
     AutoFallibleTArray<PRUint8,16384> buffer;
     nsresult rv = GetFontTable(TTAG_cmap, buffer);
     
     if (NS_SUCCEEDED(rv)) {
         bool unicodeFont;
         bool symbolFont;
         rv = gfxFontUtils::ReadCMAP(buffer.Elements(), buffer.Length(),
-                                    mCharacterMap, mUVSOffset,
+                                    *charmap, mUVSOffset,
                                     unicodeFont, symbolFont);
     }
 
     mHasCmapTable = NS_SUCCEEDED(rv);
+    if (mHasCmapTable) {
+        gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+        mCharacterMap = pfl->FindCharMap(charmap);
+    } else {
+        // if error occurred, initialize to null cmap
+        mCharacterMap = new gfxCharacterMap();
+    }
     return rv;
 }
 
 nsresult
 FT2FontEntry::GetFontTable(PRUint32 aTableTag,
                            FallibleTArray<PRUint8>& aBuffer)
 {
     // Ensure existence of mFTFace
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -98,40 +98,52 @@ static PRUint32 gFontCount = 0;
 static PRUint32 gGlyphExtentsCount = 0;
 static PRUint32 gGlyphExtentsWidthsTotalSize = 0;
 static PRUint32 gGlyphExtentsSetupEagerSimple = 0;
 static PRUint32 gGlyphExtentsSetupEagerTight = 0;
 static PRUint32 gGlyphExtentsSetupLazyTight = 0;
 static PRUint32 gGlyphExtentsSetupFallBackToTight = 0;
 #endif
 
+void
+gfxCharacterMap::NotifyReleased()
+{
+    gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList();
+    if (mShared) {
+        fontlist->RemoveCmap(this);
+    }
+    delete this;
+}
+
 gfxFontEntry::~gfxFontEntry() 
 {
     delete mUserFontData;
 }
 
 bool gfxFontEntry::IsSymbolFont() 
 {
     return mSymbolFont;
 }
 
 bool gfxFontEntry::TestCharacterMap(PRUint32 aCh)
 {
-    if (!mCmapInitialized) {
+    if (!mCharacterMap) {
         ReadCMAP();
-    }
-    return mCharacterMap.test(aCh);
+        NS_ASSERTION(mCharacterMap, "failed to initialize character map");
+    }
+    return mCharacterMap->test(aCh);
 }
 
 nsresult gfxFontEntry::InitializeUVSMap()
 {
     // mUVSOffset will not be initialized
     // until cmap is initialized.
-    if (!mCmapInitialized) {
+    if (!mCharacterMap) {
         ReadCMAP();
+        NS_ASSERTION(mCharacterMap, "failed to initialize character map");
     }
 
     if (!mUVSOffset) {
         return NS_ERROR_FAILURE;
     }
 
     if (!mUVSData) {
         const PRUint32 kCmapTag = TRUETYPE_TAG('c','m','a','p');
@@ -165,17 +177,18 @@ PRUint16 gfxFontEntry::GetUVSGlyph(PRUin
         return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData, aCh, aVS);
     }
 
     return 0;
 }
 
 nsresult gfxFontEntry::ReadCMAP()
 {
-    mCmapInitialized = true;
+    NS_ASSERTION(false, "using default no-op implementation of ReadCMAP");
+    mCharacterMap = new gfxCharacterMap();
     return NS_OK;
 }
 
 nsString gfxFontEntry::FamilyName() const
 {
     NS_ASSERTION(mFamily, "orphaned font entry");
     if (mFamily) {
         return mFamily->Name();
@@ -428,17 +441,22 @@ gfxFontEntry::FontTableHashEntry::SizeOf
     return 0;
 }
 
 void
 gfxFontEntry::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                   FontListSizes*    aSizes) const
 {
     aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
-    aSizes->mCharMapsSize += mCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
+
+    // cmaps are shared so only non-shared cmaps are included here
+    if (mCharacterMap && mCharacterMap->mBuildOnTheFly) {
+        aSizes->mCharMapsSize +=
+            mCharacterMap->SizeOfIncludingThis(aMallocSizeOf);
+    }
     aSizes->mFontTableCacheSize +=
         mFontTableCache.SizeOfExcludingThis(
             FontTableHashEntry::SizeOfEntryExcludingThis,
             aMallocSizeOf, aSizes);
 }
 
 void
 gfxFontEntry::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
@@ -765,17 +783,17 @@ CalcStyleMatch(gfxFontEntry *aFontEntry,
     return rank;
 }
 
 #define RANK_MATCHED_CMAP   20
 
 void
 gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData)
 {
-    if (mCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
+    if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
         // none of the faces in the family support the required char,
         // so bail out immediately
         return;
     }
 
     bool needsBold;
     gfxFontStyle normal;
     gfxFontEntry *fe = FindFontForStyle(
@@ -1023,17 +1041,18 @@ gfxFontFamily::FindFont(const nsAString&
 }
 
 void
 gfxFontFamily::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                    FontListSizes*    aSizes) const
 {
     aSizes->mFontListSize +=
         mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
-    aSizes->mCharMapsSize += mCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
+    aSizes->mCharMapsSize +=
+        mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
 
     aSizes->mFontListSize +=
         mAvailableFonts.SizeOfExcludingThis(aMallocSizeOf);
     for (PRUint32 i = 0; i < mAvailableFonts.Length(); ++i) {
         gfxFontEntry *fe = mAvailableFonts[i];
         if (fe) {
             fe->SizeOfIncludingThis(aMallocSizeOf, aSizes);
         }
@@ -3642,17 +3661,19 @@ gfxFontGroup::FindFontForChar(PRUint32 a
         nsRefPtr<gfxFont> font = GetFontAt(i);
         if (font->HasCharacter(aCh)) {
             *aMatchType = gfxTextRange::kFontGroup;
             return font.forget();
         }
 
         // check other faces of the family
         gfxFontFamily *family = font->GetFontEntry()->Family();
-        if (family && family->TestCharacterMap(aCh)) {
+        if (family && !font->GetFontEntry()->mIsProxy &&
+            family->TestCharacterMap(aCh))
+        {
             GlobalFontMatch matchData(aCh, aRunScript, &mStyle);
             family->SearchAllFontsForChar(&matchData);
             gfxFontEntry *fe = matchData.mBestMatch;
             if (fe) {
                 bool needsBold =
                     font->GetStyle()->weight >= 600 && !fe->IsBold();
                 selectedFont =
                     fe->FindOrMakeFont(font->GetStyle(), needsBold);
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -193,16 +193,66 @@ struct THEBES_API gfxFontStyle {
     }
 
     static void ParseFontFeatureSettings(const nsString& aFeatureString,
                                          nsTArray<gfxFontFeature>& aFeatures);
 
     static PRUint32 ParseFontLanguageOverride(const nsString& aLangTag);
 };
 
+class gfxCharacterMap : public gfxSparseBitSet {
+public:
+    nsrefcnt AddRef() {
+        NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
+        ++mRefCnt;
+        NS_LOG_ADDREF(this, mRefCnt, "gfxCharacterMap", sizeof(*this));
+        return mRefCnt;
+    }
+
+    nsrefcnt Release() {
+        NS_PRECONDITION(0 != mRefCnt, "dup release");
+        --mRefCnt;
+        NS_LOG_RELEASE(this, mRefCnt, "gfxCharacterMap");
+        if (mRefCnt == 0) {
+            NotifyReleased();
+            // |this| has been deleted.
+            return 0;
+        }
+        return mRefCnt;
+    }
+
+    gfxCharacterMap() :
+        mHash(0), mBuildOnTheFly(false), mShared(false)
+    { }
+
+    void CalcHash() { mHash = GetChecksum(); }
+
+    size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const {
+        return gfxSparseBitSet::SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+    // hash of the cmap bitvector
+    PRUint32 mHash;
+
+    // if cmap is built on the fly it's never shared
+    bool mBuildOnTheFly;
+
+    // cmap is shared globally
+    bool mShared;
+
+protected:
+    void NotifyReleased();
+
+    nsAutoRefCnt mRefCnt;
+
+private:
+    gfxCharacterMap(const gfxCharacterMap&);
+    gfxCharacterMap& operator=(const gfxCharacterMap&);
+};
+
 class gfxFontEntry {
 public:
     NS_INLINE_DECL_REFCOUNTING(gfxFontEntry)
 
     gfxFontEntry(const nsAString& aName, gfxFontFamily *aFamily = nsnull,
                  bool aIsStandardFace = false) : 
         mName(aName), mItalic(false), mFixedPitch(false),
         mIsProxy(false), mIsValid(true), 
@@ -211,17 +261,16 @@ public:
         mSymbolFont(false),
         mIgnoreGDEF(false),
         mIgnoreGSUB(false),
         mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
 #ifdef MOZ_GRAPHITE
         mCheckedForGraphiteTables(false),
 #endif
         mHasCmapTable(false),
-        mCmapInitialized(false),
         mUVSOffset(0), mUVSData(nsnull),
         mUserFontData(nsnull),
         mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
         mFamily(aFamily)
     { }
 
     virtual ~gfxFontEntry();
 
@@ -254,26 +303,27 @@ public:
             CheckForGraphiteTables();
             mCheckedForGraphiteTables = true;
         }
         return mHasGraphiteTables;
     }
 #endif
 
     inline bool HasCmapTable() {
-        if (!mCmapInitialized) {
+        if (!mCharacterMap) {
             ReadCMAP();
+            NS_ASSERTION(mCharacterMap, "failed to initialize character map");
         }
         return mHasCmapTable;
     }
 
     inline bool HasCharacter(PRUint32 ch) {
-        if (mCharacterMap.test(ch))
+        if (mCharacterMap && mCharacterMap->test(ch)) {
             return true;
-
+        }
         return TestCharacterMap(ch);
     }
 
     virtual bool SkipDuringSystemFallback() { return false; }
     virtual bool TestCharacterMap(PRUint32 aCh);
     nsresult InitializeUVSMap();
     PRUint16 GetUVSGlyph(PRUint32 aCh, PRUint32 aVS);
     virtual nsresult ReadCMAP();
@@ -339,18 +389,17 @@ public:
     PRUint16         mWeight;
     PRInt16          mStretch;
 
 #ifdef MOZ_GRAPHITE
     bool             mHasGraphiteTables;
     bool             mCheckedForGraphiteTables;
 #endif
     bool             mHasCmapTable;
-    bool             mCmapInitialized;
-    gfxSparseBitSet  mCharacterMap;
+    nsRefPtr<gfxCharacterMap> mCharacterMap;
     PRUint32         mUVSOffset;
     nsAutoArrayPtr<PRUint8> mUVSData;
     gfxUserFontData* mUserFontData;
 
     nsTArray<gfxFontFeature> mFeatureSettings;
     PRUint32         mLanguageOverride;
 
 protected:
@@ -370,17 +419,16 @@ protected:
         mSymbolFont(false),
         mIgnoreGDEF(false),
         mIgnoreGSUB(false),
         mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
 #ifdef MOZ_GRAPHITE
         mCheckedForGraphiteTables(false),
 #endif
         mHasCmapTable(false),
-        mCmapInitialized(false),
         mUVSOffset(0), mUVSData(nsnull),
         mUserFontData(nsnull),
         mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
         mFamily(nsnull)
     { }
 
     virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold) {
         NS_NOTREACHED("oops, somebody didn't override CreateFontInstance");
@@ -524,17 +572,17 @@ public:
     gfxFontFamily(const nsAString& aName) :
         mName(aName),
         mOtherFamilyNamesInitialized(false),
         mHasOtherFamilyNames(false),
         mFaceNamesInitialized(false),
         mHasStyles(false),
         mIsSimpleFamily(false),
         mIsBadUnderlineFamily(false),
-        mCharacterMapInitialized(false)
+        mFamilyCharacterMapInitialized(false)
         { }
 
     virtual ~gfxFontFamily() {
         // clear Family pointers in our faces; the font entries might stay
         // alive due to cached font objects, but they can no longer refer
         // to their families.
         PRUint32 i = mAvailableFonts.Length();
         while (i) {
@@ -602,36 +650,37 @@ public:
     // search for a specific face using the Postscript name
     gfxFontEntry* FindFont(const nsAString& aPostscriptName);
 
     // read in cmaps for all the faces
     void ReadAllCMAPs() {
         PRUint32 i, numFonts = mAvailableFonts.Length();
         for (i = 0; i < numFonts; i++) {
             gfxFontEntry *fe = mAvailableFonts[i];
-            if (!fe) {
+            // don't try to load cmaps for downloadable fonts not yet loaded
+            if (!fe || fe->mIsProxy) {
                 continue;
             }
             fe->ReadCMAP();
-            mCharacterMap.Union(fe->mCharacterMap);
+            mFamilyCharacterMap.Union(*(fe->mCharacterMap));
         }
-        mCharacterMap.Compact();
-        mCharacterMapInitialized = true;
+        mFamilyCharacterMap.Compact();
+        mFamilyCharacterMapInitialized = true;
     }
 
     bool TestCharacterMap(PRUint32 aCh) {
-        if (!mCharacterMapInitialized) {
+        if (!mFamilyCharacterMapInitialized) {
             ReadAllCMAPs();
         }
-        return mCharacterMap.test(aCh);
+        return mFamilyCharacterMap.test(aCh);
     }
 
     void ResetCharacterMap() {
-        mCharacterMap.reset();
-        mCharacterMapInitialized = false;
+        mFamilyCharacterMap.reset();
+        mFamilyCharacterMapInitialized = false;
     }
 
     // mark this family as being in the "bad" underline offset blacklist
     void SetBadUnderlineFamily() {
         mIsBadUnderlineFamily = true;
         if (mHasStyles) {
             SetBadUnderlineFonts();
         }
@@ -670,24 +719,24 @@ protected:
             if (mAvailableFonts[i]) {
                 mAvailableFonts[i]->mIsBadUnderlineFont = true;
             }
         }
     }
 
     nsString mName;
     nsTArray<nsRefPtr<gfxFontEntry> >  mAvailableFonts;
-    gfxSparseBitSet mCharacterMap;
-    bool mOtherFamilyNamesInitialized;
-    bool mHasOtherFamilyNames;
-    bool mFaceNamesInitialized;
-    bool mHasStyles;
-    bool mIsSimpleFamily;
-    bool mIsBadUnderlineFamily;
-    bool mCharacterMapInitialized;
+    gfxSparseBitSet mFamilyCharacterMap;
+    bool mOtherFamilyNamesInitialized : 1;
+    bool mHasOtherFamilyNames : 1;
+    bool mFaceNamesInitialized : 1;
+    bool mHasStyles : 1;
+    bool mIsSimpleFamily : 1;
+    bool mIsBadUnderlineFamily : 1;
+    bool mFamilyCharacterMapInitialized : 1;
 
     enum {
         // for "simple" families, the faces are stored in mAvailableFonts
         // with fixed positions:
         kRegularFaceIndex    = 0,
         kBoldFaceIndex       = 1,
         kItalicFaceIndex     = 2,
         kBoldItalicFaceIndex = 3,
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -53,16 +53,18 @@
 #include "nsCOMPtr.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsIStreamBufferAccess.h"
 
+#include "zlib.h"
+
 /* Bug 341128 - w32api defines min/max which causes problems with <bitset> */
 #ifdef __MINGW32__
 #undef min
 #undef max
 #endif
 
 class gfxSparseBitSet {
 private:
@@ -82,16 +84,38 @@ public:
         PRUint32 len = aBitset.mBlocks.Length();
         mBlocks.AppendElements(len);
         for (PRUint32 i = 0; i < len; ++i) {
             Block *block = aBitset.mBlocks[i];
             if (block)
                 mBlocks[i] = new Block(*block);
         }
     }
+
+    bool Equals(const gfxSparseBitSet *aOther) const {
+        if (mBlocks.Length() != aOther->mBlocks.Length()) {
+            return false;
+        }
+        size_t n = mBlocks.Length();
+        for (size_t i = 0; i < n; ++i) {
+            const Block *b1 = mBlocks[i];
+            const Block *b2 = aOther->mBlocks[i];
+            if (!b1 != !b2) {
+                return false;
+            }
+            if (!b1) {
+                continue;
+            }
+            if (memcmp(&b1->mBits, &b2->mBits, BLOCK_SIZE) != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     bool test(PRUint32 aIndex) const {
         NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad");
         PRUint32 blockIndex = aIndex/BLOCK_SIZE_BITS;
         if (blockIndex >= mBlocks.Length())
             return false;
         Block *block = mBlocks[blockIndex];
         if (!block)
             return false;
@@ -316,16 +340,28 @@ public:
             }
         }
     }
 
     void Compact() {
         mBlocks.Compact();
     }
 
+    PRUint32 GetChecksum() const {
+        PRUint32 check = adler32(0, Z_NULL, 0);
+        for (PRUint32 i = 0; i < mBlocks.Length(); i++) {
+            if (mBlocks[i]) {
+                const Block *block = mBlocks[i];
+                check = adler32(check, (PRUint8*) (&i), 4);
+                check = adler32(check, (PRUint8*) block, sizeof(Block));
+            }
+        }
+        return check;
+    }
+
 private:
     nsTArray< nsAutoPtr<Block> > mBlocks;
 };
 
 #define TRUETYPE_TAG(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d))
 
 namespace mozilla {
 
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -196,70 +196,89 @@ FontTypeToOutPrecision(PRUint8 fontType)
 
 GDIFontEntry::GDIFontEntry(const nsAString& aFaceName,
                            gfxWindowsFontType aFontType,
                            bool aItalic, PRUint16 aWeight, PRInt16 aStretch,
                            gfxUserFontData *aUserFontData)
     : gfxFontEntry(aFaceName),
       mWindowsFamily(0), mWindowsPitch(0),
       mFontType(aFontType),
-      mForceGDI(false), mUnknownCMAP(false),
+      mForceGDI(false),
       mCharset(), mUnicodeRanges()
 {
     mUserFontData = aUserFontData;
     mItalic = aItalic;
     mWeight = aWeight;
     mStretch = aStretch;
     if (IsType1())
         mForceGDI = true;
     mIsUserFont = aUserFontData != nsnull;
 
     InitLogFont(aFaceName, aFontType);
 }
 
 nsresult
 GDIFontEntry::ReadCMAP()
 {
+    // attempt this once, if errors occur leave a blank cmap
+    if (mCharacterMap) {
+        return NS_OK;
+    }
+
     // skip non-SFNT fonts completely
     if (mFontType != GFX_FONT_TYPE_PS_OPENTYPE && 
         mFontType != GFX_FONT_TYPE_TT_OPENTYPE &&
         mFontType != GFX_FONT_TYPE_TRUETYPE) 
     {
+        mCharacterMap = new gfxCharacterMap();
         return NS_ERROR_FAILURE;
     }
 
-    // attempt this once, if errors occur leave a blank cmap
-    if (mCmapInitialized)
-        return NS_OK;
-    mCmapInitialized = true;
+    nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+
+    PRUint32 kCMAP = TRUETYPE_TAG('c','m','a','p');
+    nsresult rv;
+
+    AutoFallibleTArray<PRUint8,16384> cmap;
+    rv = GetFontTable(kCMAP, cmap);
+
+    bool unicodeFont = false, symbolFont = false; // currently ignored
 
-    const PRUint32 kCmapTag = TRUETYPE_TAG('c','m','a','p');
-    AutoFallibleTArray<PRUint8,16384> buffer;
-    if (GetFontTable(kCmapTag, buffer) != NS_OK)
-        return NS_ERROR_FAILURE;
-    PRUint8 *cmap = buffer.Elements();
+    if (NS_SUCCEEDED(rv)) {
+        rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
+                                    *charmap, mUVSOffset,
+                                    unicodeFont, symbolFont);
+    }
+    mSymbolFont = symbolFont;
 
-    bool          unicodeFont = false, symbolFont = false;
-    nsresult rv = gfxFontUtils::ReadCMAP(cmap, buffer.Length(),
-                                         mCharacterMap, mUVSOffset,
-                                         unicodeFont, symbolFont);
-    mSymbolFont = symbolFont;
     mHasCmapTable = NS_SUCCEEDED(rv);
+    if (mHasCmapTable) {
+        gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+        mCharacterMap = pfl->FindCharMap(charmap);
+    } else {
+        // if error occurred, initialize to null cmap
+        mCharacterMap = new gfxCharacterMap();
+        // For fonts where we failed to read the character map,
+        // we can take a slow path to look up glyphs character by character
+        mCharacterMap->mBuildOnTheFly = true;
+    }
 
 #ifdef PR_LOGGING
-    LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n",
+    LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
                   NS_ConvertUTF16toUTF8(mName).get(),
-                  mCharacterMap.SizeOfExcludingThis(moz_malloc_size_of)));
+                  charmap->SizeOfIncludingThis(moz_malloc_size_of),
+                  charmap->mHash, mCharacterMap == charmap ? " new" : ""));
     if (LOG_CMAPDATA_ENABLED()) {
         char prefix[256];
         sprintf(prefix, "(cmapdata) name: %.220s",
                 NS_ConvertUTF16toUTF8(mName).get());
-        mCharacterMap.Dump(prefix, eGfxLog_cmapdata);
+        charmap->Dump(prefix, eGfxLog_cmapdata);
     }
 #endif
+
     return rv;
 }
 
 bool
 GDIFontEntry::IsSymbolFont()
 {
     // initialize cmap first
     HasCmapTable();
@@ -341,23 +360,22 @@ GDIFontEntry::FillLogFont(LOGFONTW *aLog
 }
 
 #define MISSING_GLYPH 0x1F // glyph index returned for missing characters
                            // on WinXP with .fon fonts, but not Type1 (.pfb)
 
 bool 
 GDIFontEntry::TestCharacterMap(PRUint32 aCh)
 {
-    if (ReadCMAP() != NS_OK) {
-        // For fonts where we failed to read the character map,
-        // we can take a slow path to look up glyphs character by character
-        mUnknownCMAP = true;
+    if (!mCharacterMap) {
+        nsresult rv = ReadCMAP();
+        NS_ASSERTION(mCharacterMap, "failed to initialize a character map");
     }
 
-    if (mUnknownCMAP) {
+    if (mCharacterMap->mBuildOnTheFly) {
         if (aCh > 0xFFFF)
             return false;
 
         // previous code was using the group style
         gfxFontStyle fakeStyle;  
         if (mItalic)
             fakeStyle.style = NS_FONT_STYLE_ITALIC;
         fakeStyle.weight = mWeight * 100;
@@ -399,22 +417,22 @@ GDIFontEntry::TestCharacterMap(PRUint32 
             if (rv == S_OK)
                 hasGlyph = true;
         }
 
         SelectObject(dc, oldFont);
         ReleaseDC(NULL, dc);
 
         if (hasGlyph) {
-            mCharacterMap.set(aCh);
+            mCharacterMap->set(aCh);
             return true;
         }
     } else {
         // font had a cmap so simply check that
-        return mCharacterMap.test(aCh);
+        return mCharacterMap->test(aCh);
     }
 
     return false;
 }
 
 void
 GDIFontEntry::InitLogFont(const nsAString& aName,
                           gfxWindowsFontType aFontType)
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -288,17 +288,16 @@ public:
     static GDIFontEntry* LoadLocalFont(const gfxProxyFontEntry &aProxyEntry,
                                        const nsAString& aFullname);
 
     PRUint8 mWindowsFamily;
     PRUint8 mWindowsPitch;
 
     gfxWindowsFontType mFontType;
     bool mForceGDI    : 1;
-    bool mUnknownCMAP : 1;
 
     gfxSparseBitSet mCharset;
     gfxSparseBitSet mUnicodeRanges;
 
 protected:
     friend class gfxWindowsFont;
 
     GDIFontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType,
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -184,103 +184,108 @@ const ScriptRange gScriptsThatRequireSha
     // Thai seems to be "renderable" without AAT morphing tables
     // xxx - Lao, Khmer?
 };
 
 nsresult
 MacOSFontEntry::ReadCMAP()
 {
     // attempt this once, if errors occur leave a blank cmap
-    if (mCmapInitialized) {
+    if (mCharacterMap) {
         return NS_OK;
     }
-    mCmapInitialized = true;
-
-    PRUint32 kCMAP = TRUETYPE_TAG('c','m','a','p');
-
-    AutoFallibleTArray<PRUint8,16384> cmap;
-    if (GetFontTable(kCMAP, cmap) != NS_OK) {
-        return NS_ERROR_FAILURE;
-    }
 
-    bool          unicodeFont, symbolFont; // currently ignored
-    nsresult rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
-                                         mCharacterMap, mUVSOffset,
-                                         unicodeFont, symbolFont);
-    if (NS_FAILED(rv)) {
-        mCharacterMap.reset();
-        return rv;
-    }
-    mHasCmapTable = true;
+    nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
+
+    PRUint32 kCMAP = TRUETYPE_TAG('c','m','a','p');
+    nsresult rv;
 
-    CGFontRef fontRef = GetFontRef();
-    if (!fontRef) {
-        return NS_ERROR_FAILURE;
+    AutoFallibleTArray<PRUint8,16384> cmap;
+    rv = GetFontTable(kCMAP, cmap);
+
+    bool unicodeFont = false, symbolFont = false; // currently ignored
+
+    if (NS_SUCCEEDED(rv)) {
+        rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
+                                    *charmap, mUVSOffset,
+                                    unicodeFont, symbolFont);
     }
 
-    // for layout support, check for the presence of mort/morx and/or
-    // opentype layout tables
-    bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m','o','r','x')) ||
-                          HasFontTable(TRUETYPE_TAG('m','o','r','t'));
-    bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
-    bool hasGPOS = HasFontTable(TRUETYPE_TAG('G','P','O','S'));
-
-    if (hasAATLayout && !(hasGSUB || hasGPOS)) {
-        mRequiresAAT = true; // prefer CoreText if font has no OTL tables
-    }
+    if (NS_SUCCEEDED(rv)) {
+        // for layout support, check for the presence of mort/morx and/or
+        // opentype layout tables
+        bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m','o','r','x')) ||
+                              HasFontTable(TRUETYPE_TAG('m','o','r','t'));
+        bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
+        bool hasGPOS = HasFontTable(TRUETYPE_TAG('G','P','O','S'));
 
-    PRUint32 numScripts =
-        sizeof(gScriptsThatRequireShaping) / sizeof(ScriptRange);
+        if (hasAATLayout && !(hasGSUB || hasGPOS)) {
+            mRequiresAAT = true; // prefer CoreText if font has no OTL tables
+        }
+
+        PRUint32 numScripts =
+            sizeof(gScriptsThatRequireShaping) / sizeof(ScriptRange);
 
-    for (PRUint32 s = 0; s < numScripts; s++) {
-        eComplexScript  whichScript = gScriptsThatRequireShaping[s].script;
+        for (PRUint32 s = 0; s < numScripts; s++) {
+            eComplexScript  whichScript = gScriptsThatRequireShaping[s].script;
 
-        // check to see if the cmap includes complex script codepoints
-        if (mCharacterMap.TestRange(gScriptsThatRequireShaping[s].rangeStart,
-                                    gScriptsThatRequireShaping[s].rangeEnd)) {
-            bool omitRange = true;
+            // check to see if the cmap includes complex script codepoints
+            if (charmap->TestRange(gScriptsThatRequireShaping[s].rangeStart,
+                                   gScriptsThatRequireShaping[s].rangeEnd)) {
+                bool omitRange = true;
 
-            if (hasAATLayout) {
-                omitRange = false;
-                // prefer CoreText for Apple's complex-script fonts,
-                // even if they also have some OpenType tables
-                // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
-                mRequiresAAT = true;
-            } else if (whichScript == eComplexScriptArabic) {
-                // special-case for Arabic:
-                // even if there's no morph table, CoreText can shape Arabic
-                // using OpenType layout; or if it's a downloaded font,
-                // assume the site knows what it's doing (as harfbuzz will
-                // be able to shape even though the font itself lacks tables
-                // stripped during sanitization).
-                // We check for GSUB here, as GPOS alone would not be ok
-                // for Arabic shaping.
-                if (hasGSUB || (mIsUserFont && !mIsLocalUserFont)) {
-                    // TODO: to be really thorough, we could check that the
-                    // GSUB table actually supports the 'arab' script tag.
+                if (hasAATLayout) {
                     omitRange = false;
+                    // prefer CoreText for Apple's complex-script fonts,
+                    // even if they also have some OpenType tables
+                    // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
+                    mRequiresAAT = true;
+                } else if (whichScript == eComplexScriptArabic) {
+                    // special-case for Arabic:
+                    // even if there's no morph table, CoreText can shape Arabic
+                    // using OpenType layout; or if it's a downloaded font,
+                    // assume the site knows what it's doing (as harfbuzz will
+                    // be able to shape even though the font itself lacks tables
+                    // stripped during sanitization).
+                    // We check for GSUB here, as GPOS alone would not be ok
+                    // for Arabic shaping.
+                    if (hasGSUB || (mIsUserFont && !mIsLocalUserFont)) {
+                        // TODO: to be really thorough, we could check that the
+                        // GSUB table actually supports the 'arab' script tag.
+                        omitRange = false;
+                    }
                 }
-            }
 
-            if (omitRange) {
-                mCharacterMap.ClearRange(gScriptsThatRequireShaping[s].rangeStart,
-                                         gScriptsThatRequireShaping[s].rangeEnd);
+                if (omitRange) {
+                    charmap->ClearRange(gScriptsThatRequireShaping[s].rangeStart,
+                                        gScriptsThatRequireShaping[s].rangeEnd);
+                }
             }
         }
     }
 
+    mHasCmapTable = NS_SUCCEEDED(rv);
+    if (mHasCmapTable) {
+        gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
+        mCharacterMap = pfl->FindCharMap(charmap);
+    } else {
+        // if error occurred, initialize to null cmap
+        mCharacterMap = new gfxCharacterMap();
+    }
+
 #ifdef PR_LOGGING
-    LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n",
+    LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
                   NS_ConvertUTF16toUTF8(mName).get(),
-                  mCharacterMap.SizeOfExcludingThis(moz_malloc_size_of)));
+                  charmap->SizeOfIncludingThis(moz_malloc_size_of),
+                  charmap->mHash, mCharacterMap == charmap ? " new" : ""));
     if (LOG_CMAPDATA_ENABLED()) {
         char prefix[256];
         sprintf(prefix, "(cmapdata) name: %.220s",
                 NS_ConvertUTF16toUTF8(mName).get());
-        mCharacterMap.Dump(prefix, eGfxLog_cmapdata);
+        charmap->Dump(prefix, eGfxLog_cmapdata);
     }
 #endif
 
     return rv;
 }
 
 gfxFont*
 MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -208,20 +208,23 @@ gfxPlatformFontList::gfxPlatformFontList
     LoadBadUnderlineList();
 
     // pref changes notification setup
     NS_ASSERTION(!gFontListPrefObserver,
                  "There has been font list pref observer already");
     gFontListPrefObserver = new gfxFontListPrefObserver();
     NS_ADDREF(gFontListPrefObserver);
     Preferences::AddStrongObservers(gFontListPrefObserver, kObservedPrefs);
+
+    mSharedCmaps.Init(16);
 }
 
 gfxPlatformFontList::~gfxPlatformFontList()
 {
+    mSharedCmaps.Clear();
     NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
     Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs);
     NS_RELEASE(gFontListPrefObserver);
 }
 
 nsresult
 gfxPlatformFontList::InitFontList()
 {
@@ -709,16 +712,51 @@ gfxPlatformFontList::AddPostscriptName(g
 bool
 gfxPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
 {
     aFamilyName.Truncate();
     ResolveFontName(aFontName, aFamilyName);
     return !aFamilyName.IsEmpty();
 }
 
+gfxCharacterMap*
+gfxPlatformFontList::FindCharMap(gfxCharacterMap *aCmap)
+{
+    aCmap->CalcHash();
+    gfxCharacterMap *cmap = AddCmap(aCmap);
+    cmap->mShared = true;
+    return cmap;
+}
+
+// add a cmap to the shared cmap set
+gfxCharacterMap*
+gfxPlatformFontList::AddCmap(const gfxCharacterMap* aCharMap)
+{
+    CharMapHashKey *found =
+        mSharedCmaps.PutEntry(const_cast<gfxCharacterMap*>(aCharMap));
+    return found->GetKey();
+}
+
+// remove the cmap from the shared cmap set
+void
+gfxPlatformFontList::RemoveCmap(const gfxCharacterMap* aCharMap)
+{
+    // skip lookups during teardown
+    if (mSharedCmaps.Count() == 0) {
+        return;
+    }
+
+    // cmap needs to match the entry *and* be the same ptr before removing
+    CharMapHashKey *found =
+        mSharedCmaps.GetEntry(const_cast<gfxCharacterMap*>(aCharMap));
+    if (found && found->GetKey() == aCharMap) {
+        mSharedCmaps.RemoveEntry(const_cast<gfxCharacterMap*>(aCharMap));
+    }
+}
+
 void 
 gfxPlatformFontList::InitLoader()
 {
     GetFontFamilyList(mFontFamiliesToLoad);
     mStartIndex = 0;
     mNumFamilies = mFontFamiliesToLoad.Length();
 }
 
@@ -825,16 +863,31 @@ SizeOfPrefFontEntryExcludingThis
 static size_t
 SizeOfStringEntryExcludingThis(nsStringHashKey*  aHashEntry,
                                nsMallocSizeOfFun aMallocSizeOf,
                                void*             aUserArg)
 {
     return aHashEntry->GetKey().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
 }
 
+static size_t
+SizeOfSharedCmapExcludingThis(CharMapHashKey*   aHashEntry,
+                              nsMallocSizeOfFun aMallocSizeOf,
+                              void*             aUserArg)
+{
+    FontListSizes *sizes = static_cast<FontListSizes*>(aUserArg);
+
+    PRUint32 size = aHashEntry->GetKey()->SizeOfIncludingThis(aMallocSizeOf);
+    sizes->mCharMapsSize += size;
+
+    // we return zero here because the measurements have been added directly
+    // to the relevant fields of the FontListSizes record
+    return 0;
+}
+
 void
 gfxPlatformFontList::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                          FontListSizes*    aSizes) const
 {
     aSizes->mFontListSize +=
         mFontFamilies.SizeOfExcludingThis(SizeOfFamilyEntryExcludingThis,
                                           aMallocSizeOf, aSizes);
 
@@ -860,16 +913,20 @@ gfxPlatformFontList::SizeOfExcludingThis
 
     aSizes->mFontListSize +=
         mPrefFonts.SizeOfExcludingThis(SizeOfPrefFontEntryExcludingThis,
                                        aMallocSizeOf);
 
     aSizes->mFontListSize +=
         mBadUnderlineFamilyNames.SizeOfExcludingThis(SizeOfStringEntryExcludingThis,
                                                      aMallocSizeOf);
+
+    aSizes->mFontListSize +=
+        mSharedCmaps.SizeOfExcludingThis(SizeOfSharedCmapExcludingThis,
+                                         aMallocSizeOf, aSizes);
 }
 
 void
 gfxPlatformFontList::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                          FontListSizes*    aSizes) const
 {
     aSizes->mFontListSize += aMallocSizeOf(this);
     SizeOfExcludingThis(aMallocSizeOf, aSizes);
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -45,16 +45,63 @@
 
 #include "gfxFontUtils.h"
 #include "gfxFont.h"
 #include "gfxPlatform.h"
 
 #include "nsIMemoryReporter.h"
 #include "mozilla/FunctionTimer.h"
 
+class CharMapHashKey : public PLDHashEntryHdr
+{
+public:
+    typedef gfxCharacterMap* KeyType;
+    typedef const gfxCharacterMap* KeyTypePointer;
+
+    CharMapHashKey(const gfxCharacterMap *aCharMap) :
+        mCharMap(const_cast<gfxCharacterMap*>(aCharMap))
+    {
+        MOZ_COUNT_CTOR(CharMapHashKey);
+    }
+    CharMapHashKey(const CharMapHashKey& toCopy) :
+        mCharMap(toCopy.mCharMap)
+    {
+        MOZ_COUNT_CTOR(CharMapHashKey);
+    }
+    ~CharMapHashKey()
+    {
+        MOZ_COUNT_DTOR(CharMapHashKey);
+    }
+
+    gfxCharacterMap* GetKey() const { return mCharMap; }
+
+    bool KeyEquals(const gfxCharacterMap *aCharMap) const {
+        NS_ASSERTION(!aCharMap->mBuildOnTheFly && !mCharMap->mBuildOnTheFly,
+                     "custom cmap used in shared cmap hashtable");
+        // cmaps built on the fly never match
+        if (aCharMap->mHash != mCharMap->mHash)
+        {
+            return false;
+        }
+        return mCharMap->Equals(aCharMap);
+    }
+
+    static const gfxCharacterMap* KeyToPointer(gfxCharacterMap *aCharMap) {
+        return aCharMap;
+    }
+    static PLDHashNumber HashKey(const gfxCharacterMap *aCharMap) {
+        return aCharMap->mHash;
+    }
+
+    enum { ALLOW_MEMMOVE = true };
+
+protected:
+    gfxCharacterMap *mCharMap;
+};
+
 // gfxPlatformFontList is an abstract class for the global font list on the system;
 // concrete subclasses for each platform implement the actual interface to the system fonts.
 // This class exists because we cannot rely on the platform font-finding APIs to behave
 // in sensible/similar ways, particularly with rich, complex OpenType families,
 // so we do our own font family/style management here instead.
 
 // Much of this is based on the old gfxQuartzFontCache, but adapted for use on all platforms.
 
@@ -150,16 +197,26 @@ public:
     // (platforms may override, eg Mac)
     virtual bool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
 
     virtual void SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                      FontListSizes*    aSizes) const;
     virtual void SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf,
                                      FontListSizes*    aSizes) const;
 
+    // search for existing cmap that matches the input
+    // return the input if no match is found
+    gfxCharacterMap* FindCharMap(gfxCharacterMap *aCmap);
+
+    // add a cmap to the shared cmap set
+    gfxCharacterMap* AddCmap(const gfxCharacterMap *aCharMap);
+
+    // remove the cmap from the shared cmap set
+    void RemoveCmap(const gfxCharacterMap *aCharMap);
+
 protected:
     class MemoryReporter
         : public nsIMemoryMultiReporter
     {
     public:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIMEMORYMULTIREPORTER
     };
@@ -259,16 +316,20 @@ protected:
     gfxSparseBitSet mCodepointsWithNoFonts;
 
     // the family to use for U+FFFD fallback, to avoid expensive search every time
     // on pages with lots of problems
     nsString mReplacementCharFallbackFamily;
 
     nsTHashtable<nsStringHashKey> mBadUnderlineFamilyNames;
 
+    // character map data shared across families
+    // contains weak ptrs to cmaps shared by font entry objects
+    nsTHashtable<CharMapHashKey> mSharedCmaps;
+
     // data used as part of the font cmap loading process
     nsTArray<nsRefPtr<gfxFontFamily> > mFontFamiliesToLoad;
     PRUint32 mStartIndex;
     PRUint32 mIncrement;
     PRUint32 mNumFamilies;
 };
 
 #endif /* GFXPLATFORMFONTLIST_H_ */