bug 671297 - add memory reporting for textRuns. r=roc,jlebar
authorJonathan Kew <jfkthame@gmail.com>
Fri, 14 Oct 2011 08:06:35 +0100
changeset 78729 56c7568767f18154d266287d6ba03264602a3133
parent 78728 aa2e380c86fb21743b7e627687357fc06761a84b
child 78730 6715771c1d9a32da64293eebccd6fcf9a07eaae0
push id21329
push usereakhgari@mozilla.com
push dateFri, 14 Oct 2011 14:37:50 +0000
treeherdermozilla-central@349f3d4b2d87 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, jlebar
bugs671297
milestone10.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 671297 - add memory reporting for textRuns. r=roc,jlebar
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxTextRunWordCache.cpp
gfx/thebes/gfxTextRunWordCache.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
layout/generic/nsTextRunTransformations.cpp
layout/generic/nsTextRunTransformations.h
xpcom/glue/nsTArray-inl.h
xpcom/glue/nsTArray.h
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3110,45 +3110,54 @@ AccountStorageForTextRun(gfxTextRun *aTe
       bytes &= ~(sizeof(gfxTextRun::CompressedGlyph) - 1);
     }
     bytes += sizeof(gfxTextRun);
     gTextRunStorage += bytes*aSign;
     gTextRunStorageHighWaterMark = NS_MAX(gTextRunStorageHighWaterMark, gTextRunStorage);
 }
 #endif
 
+static PRUint64
+GlyphStorageAllocCount(PRUint32 aLength, PRUint32 aFlags)
+{
+    // always need to allocate storage for the glyph data
+    PRUint64 allocCount = aLength;
+
+    // if the text is not persistent, we also need space for a copy
+    if (!(aFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
+        // figure out number of extra CompressedGlyph elements we need to
+        // get sufficient space for the text
+        typedef gfxTextRun::CompressedGlyph CompressedGlyph;
+        if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
+            allocCount += (aLength + sizeof(CompressedGlyph) - 1) /
+                          sizeof(CompressedGlyph);
+        } else {
+            allocCount += (aLength * sizeof(PRUnichar) +
+                              sizeof(CompressedGlyph) - 1) /
+                          sizeof(CompressedGlyph);
+        }
+    }
+    return allocCount;
+}
+
 // Helper for textRun creation to preallocate storage for glyphs and text;
 // this function returns a pointer to the newly-allocated glyph storage,
 // AND modifies the aText parameter if TEXT_IS_PERSISTENT was not set.
 // In that case, the text is appended to the glyph storage, so a single
 // delete[] operation in the textRun destructor will free both.
 // Returns nsnull if allocation fails.
 gfxTextRun::CompressedGlyph *
 gfxTextRun::AllocateStorage(const void*& aText, PRUint32 aLength, PRUint32 aFlags)
 {
     // Here, we rely on CompressedGlyph being the largest unit we care about for
     // allocation/alignment of either glyph data or text, so we allocate an array
     // of CompressedGlyphs, then take the last chunk of that and cast a pointer to
     // PRUint8* or PRUnichar* for text storage.
 
-    // always need to allocate storage for the glyph data
-    PRUint64 allocCount = aLength;
-
-    // if the text is not persistent, we also need space for a copy
-    if (!(aFlags & gfxTextRunFactory::TEXT_IS_PERSISTENT)) {
-        // figure out number of extra CompressedGlyph elements we need to
-        // get sufficient space for the text
-        if (aFlags & gfxTextRunFactory::TEXT_IS_8BIT) {
-            allocCount += (aLength + sizeof(CompressedGlyph)-1)
-                          / sizeof(CompressedGlyph);
-        } else {
-            allocCount += (aLength*sizeof(PRUnichar) + sizeof(CompressedGlyph)-1)
-                          / sizeof(CompressedGlyph);
-        }
-    }
+    PRUint64 allocCount = GlyphStorageAllocCount(aLength, aFlags);
 
     // allocate the storage we need, returning nsnull on failure rather than
     // throwing an exception (because web content can create huge runs)
     CompressedGlyph *storage = new (std::nothrow) CompressedGlyph[allocCount];
     if (!storage) {
         NS_WARNING("failed to allocate glyph/text storage for text run!");
         return nsnull;
     }
@@ -4466,16 +4475,41 @@ gfxTextRun::ClusterIterator::ClusterAdva
 {
     if (mCurrentChar == PRUint32(-1)) {
         return 0;
     }
 
     return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider);
 }
 
+PRUint64
+gfxTextRun::ComputeSize()
+{
+    PRUint64 total = moz_malloc_usable_size(this);
+    if (total == 0) {
+        total = sizeof(gfxTextRun);
+    }
+
+    PRUint64 glyphDataSize = moz_malloc_usable_size(mCharacterGlyphs);
+    if (glyphDataSize == 0) {
+        // calculate how much gfxTextRun::AllocateStorage would have allocated
+        glyphDataSize = sizeof(CompressedGlyph) *
+            GlyphStorageAllocCount(mCharacterCount, mFlags);
+    }
+    total += glyphDataSize;
+
+    if (mDetailedGlyphs) {
+        total += mDetailedGlyphs->SizeOf();
+    }
+
+    total += mGlyphRuns.SizeOf();
+
+    return total;
+}
+
 
 #ifdef DEBUG
 void
 gfxTextRun::Dump(FILE* aOutput) {
     if (!aOutput) {
         aOutput = stdout;
     }
 
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1348,17 +1348,25 @@ public:
          * required for legible text should still be enabled.
          */
         TEXT_DISABLE_OPTIONAL_LIGATURES = 0x0080,
         /**
          * When set, the textrun should favour speed of construction over
          * quality. This may involve disabling ligatures and/or kerning or
          * other effects.
          */
-        TEXT_OPTIMIZE_SPEED          = 0x0100
+        TEXT_OPTIMIZE_SPEED          = 0x0100,
+        /**
+         * For internal use by the memory reporter when accounting for
+         * storage used by textruns.
+         * Because the reporter may visit each textrun multiple times while
+         * walking the frame trees and textrun cache, it needs to mark
+         * textruns that have been seen so as to avoid multiple-accounting.
+         */
+        TEXT_RUN_SIZE_ACCOUNTED      = 0x0200
     };
 
     /**
      * This record contains all the parameters needed to initialize a textrun.
      */
     struct Parameters {
         // A reference context suggesting where the textrun will be rendered
         gfxContext   *mContext;
@@ -2033,16 +2041,31 @@ public:
         
         bool mClipBeforePart;
         bool mClipAfterPart;
     };
     
     // user font set generation when text run was created
     PRUint64 GetUserFontSetGeneration() { return mUserFontSetGeneration; }
 
+    // return storage used by this run, for memory reporter;
+    // nsTransformedTextRun needs to override this as it holds additional data
+    virtual PRUint64 ComputeSize();
+
+    void AccountForSize(PRUint64* aTotal)  {
+        if (mFlags & gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED) {
+            return;
+        }
+        mFlags |= gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
+        *aTotal += ComputeSize();
+    }
+    void ClearSizeAccounted() {
+        mFlags &= ~gfxTextRunFactory::TEXT_RUN_SIZE_ACCOUNTED;
+    }
+
 #ifdef DEBUG
     // number of entries referencing this textrun in the gfxTextRunWordCache
     PRUint32 mCachedWords;
     // generation of gfxTextRunWordCache that refers to this textrun;
     // if the cache gets cleared, then mCachedWords is no longer meaningful
     PRUint32 mCacheGeneration;
     
     void Dump(FILE* aOutput);
@@ -2203,16 +2226,21 @@ private:
                 if (!mOffsetToIndex.InsertElementSorted(DGRec(aOffset, detailIndex),
                                                         CompareRecordOffsets())) {
                     return nsnull;
                 }
             }
             return details;
         }
 
+        PRUint32 SizeOf() {
+            return sizeof(DetailedGlyphStore) +
+                mDetails.SizeOf() + mOffsetToIndex.SizeOf();
+        }
+
     private:
         struct DGRec {
             DGRec(const PRUint32& aOffset, const PRUint32& aIndex)
                 : mOffset(aOffset), mIndex(aIndex) { }
             PRUint32 mOffset; // source character offset in the textrun
             PRUint32 mIndex;  // index where this char's DetailedGlyphs begin
         };
 
--- a/gfx/thebes/gfxTextRunWordCache.cpp
+++ b/gfx/thebes/gfxTextRunWordCache.cpp
@@ -130,16 +130,18 @@ public:
      */
     void Flush() {
         mCache.Clear(); 
 #ifdef DEBUG
         mGeneration++;
 #endif
     }
 
+    void ComputeStorage(PRUint64 *aTotal);
+
 #ifdef DEBUG
     PRUint32 mGeneration;
     void Dump();
 #endif
 
 protected:
     struct CacheHashKey {
         void        *mFontOrGroup;
@@ -212,16 +214,21 @@ protected:
     void FinishTextRun(gfxTextRun *aTextRun, gfxTextRun *aNewRun,
                        const gfxFontGroup::Parameters *aParams,
                        const nsTArray<DeferredWord>& aDeferredWords,
                        bool aSuccessful);
     void RemoveWord(gfxTextRun *aTextRun, PRUint32 aStart,
                     PRUint32 aEnd, PRUint32 aHash);
     void Uninit();
 
+    static PLDHashOperator AccountForStorage(CacheHashEntry *aEntry,
+                                             void *aUserData);
+    static PLDHashOperator ClearSizeAccounted(CacheHashEntry *aEntry,
+                                              void *aUserData);
+
     nsTHashtable<CacheHashEntry> mCache;
 
     PRInt32 mBidiNumeral;
 
 #ifdef DEBUG
     static PLDHashOperator CacheDumpEntry(CacheHashEntry* aEntry, void* userArg);
 #endif
 };
@@ -902,16 +909,48 @@ TextRunWordCache::RemoveTextRun(gfxTextR
     }
     RemoveWord(aTextRun, wordStart, i, hash);
 #ifdef DEBUG
     NS_ASSERTION(aTextRun->mCachedWords == 0,
                  "Textrun was not completely removed from the cache!");
 #endif
 }
 
+/*static*/ PLDHashOperator
+TextRunWordCache::AccountForStorage(CacheHashEntry *aEntry, void *aUserData)
+{
+    gfxTextRun *run = aEntry->mTextRun;
+    if (run) {
+        PRUint64 *total = static_cast<PRUint64*>(aUserData);
+        run->AccountForSize(total);
+    }
+    return PL_DHASH_NEXT;
+}
+
+/*static*/ PLDHashOperator
+TextRunWordCache::ClearSizeAccounted(CacheHashEntry *aEntry, void *)
+{
+    gfxTextRun *run = aEntry->mTextRun;
+    if (run) {
+        run->ClearSizeAccounted();
+    }
+    return PL_DHASH_NEXT;
+}
+
+void
+TextRunWordCache::ComputeStorage(PRUint64 *aTotal)
+{
+    if (aTotal) {
+        *aTotal += mCache.SizeOf();
+        mCache.EnumerateEntries(AccountForStorage, aTotal);
+    } else {
+        mCache.EnumerateEntries(ClearSizeAccounted, nsnull);
+    }
+}
+
 static bool
 CompareDifferentWidthStrings(const PRUint8 *aStr1, const PRUnichar *aStr2,
                              PRUint32 aLength)
 {
     PRUint32 i;
     for (i = 0; i < aLength; ++i) {
         if (aStr1[i] != aStr2[i])
             return PR_FALSE;
@@ -1056,8 +1095,18 @@ gfxTextRunWordCache::RemoveTextRun(gfxTe
 
 void
 gfxTextRunWordCache::Flush()
 {
     if (!gTextRunWordCache)
         return;
     gTextRunWordCache->Flush();
 }
+
+void
+gfxTextRunWordCache::ComputeStorage(PRUint64 *aTotal)
+{
+    if (!gTextRunWordCache) {
+        return;
+    }
+    gTextRunWordCache->ComputeStorage(aTotal);
+}
+
--- a/gfx/thebes/gfxTextRunWordCache.h
+++ b/gfx/thebes/gfxTextRunWordCache.h
@@ -100,16 +100,25 @@ public:
     static void RemoveTextRun(gfxTextRun *aTextRun);
 
     /**
      * Flush the textrun cache. This must be called if a configuration
      * change that would affect textruns is applied.
      */
     static void Flush();
 
+    /**
+     * If aTotal is NULL, just clears the TEXT_RUN_MEMORY_ACCOUNTED flag
+     * on each textRun found.
+     * If aTotal is non-NULL, adds the storage used for each textRun to the
+     * total, and sets the TEXT_RUN_MEMORY_ACCOUNTED flag to avoid double-
+     * accounting. (Runs with this flag already set will be skipped.)
+     */
+    static void ComputeStorage(PRUint64 *aTotal);
+
 protected:
     friend class gfxPlatform;
 
     static nsresult Init();
     static void Shutdown();
 };
 
 #endif /* GFX_TEXT_RUN_WORD_CACHE_H */
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -4278,16 +4278,49 @@ nsLayoutUtils::GetFontFacesForText(nsIFr
                                        curr);
   } while (aFollowContinuations &&
            (curr = static_cast<nsTextFrame*>(curr->GetNextContinuation())));
 
   return NS_OK;
 }
 
 /* static */
+nsresult
+nsLayoutUtils::GetTextRunMemoryForFrames(nsIFrame* aFrame, PRUint64* aTotal)
+{
+  NS_PRECONDITION(aFrame, "NULL frame pointer");
+
+  if (aFrame->GetType() == nsGkAtoms::textFrame) {
+    nsTextFrame* textFrame = static_cast<nsTextFrame*>(aFrame);
+    gfxTextRun *run = textFrame->GetTextRun();
+    if (run) {
+      if (aTotal) {
+        run->AccountForSize(aTotal);
+      } else {
+        run->ClearSizeAccounted();
+      }
+    }
+    return NS_OK;
+  }
+
+  nsAutoTArray<nsIFrame::ChildList,4> childListArray;
+  aFrame->GetChildLists(&childListArray);
+
+  for (nsIFrame::ChildListArrayIterator childLists(childListArray);
+       !childLists.IsDone(); childLists.Next()) {
+    for (nsFrameList::Enumerator e(childLists.CurrentList());
+         !e.AtEnd(); e.Next()) {
+      GetTextRunMemoryForFrames(e.get(), aTotal);
+    }
+  }
+
+  return NS_OK;
+}
+
+/* static */
 void
 nsLayoutUtils::Shutdown()
 {
   if (sContentMap) {
     delete sContentMap;
     sContentMap = NULL;
   }
 }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1429,16 +1429,30 @@ public:
    */
   static nsresult GetFontFacesForText(nsIFrame* aFrame,
                                       PRInt32 aStartOffset,
                                       PRInt32 aEndOffset,
                                       bool aFollowContinuations,
                                       nsFontFaceList* aFontFaceList);
 
   /**
+   * Walks the frame tree starting at aFrame looking for textRuns.
+   * If aTotal is NULL, just clears the TEXT_RUN_MEMORY_ACCOUNTED flag
+   * on each textRun found.
+   * If aTotal is non-NULL, adds the storage used for each textRun to the
+   * total, and sets the TEXT_RUN_MEMORY_ACCOUNTED flag to avoid double-
+   * accounting. (Runs with this flag already set will be skipped.)
+   * Expected usage pattern is therefore to call twice:
+   *    rv = GetTextRunMemoryForFrames(rootFrame, NULL);
+   *    rv = GetTextRunMemoryForFrames(rootFrame, &total);
+   */
+  static nsresult GetTextRunMemoryForFrames(nsIFrame* aFrame,
+                                            PRUint64* aTotal);
+
+  /**
    * Checks if CSS 3D transforms are currently enabled.
    */
   static bool Are3DTransformsEnabled();
 
   static void Shutdown();
 
 #ifdef DEBUG
   /**
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -215,16 +215,18 @@
 #define ANCHOR_SCROLL_FLAGS (SCROLL_OVERFLOW_HIDDEN | SCROLL_NO_PARENT_FRAMES)
 
 #include "nsContentCID.h"
 static NS_DEFINE_IID(kRangeCID,     NS_RANGE_CID);
 
 /* for NS_MEMORY_REPORTER_IMPLEMENT */
 #include "nsIMemoryReporter.h"
 
+#include "gfxTextRunWordCache.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 
 CapturingContentInfo nsIPresShell::gCaptureInfo =
   { PR_FALSE /* mAllowed */,     PR_FALSE /* mRetargetToElement */,
     PR_FALSE /* mPreventDrag */, nsnull /* mContent */ };
 nsIContent* nsIPresShell::gKeyDownTarget;
@@ -636,50 +638,79 @@ PresShell::MemoryReporter::SizeEnumerato
       str += spec;
     }
   }
 
   str += NS_LITERAL_CSTRING(")");
 
   NS_NAMED_LITERAL_CSTRING(kArenaDesc, "Memory used by layout PresShell, PresContext, and other related areas.");
   NS_NAMED_LITERAL_CSTRING(kStyleDesc, "Memory used by the style system.");
+  NS_NAMED_LITERAL_CSTRING(kTextRunsDesc, "Memory used for text-runs (glyph layout) in the PresShell's frame tree.");
 
   nsCAutoString arenaPath = str + NS_LITERAL_CSTRING("/arenas");
   nsCAutoString stylePath = str + NS_LITERAL_CSTRING("/styledata");
+  nsCAutoString textRunsPath = str + NS_LITERAL_CSTRING("/textruns");
 
   PRUint32 arenasSize;
   arenasSize = aShell->EstimateMemoryUsed();
   arenasSize += aShell->mPresContext->EstimateMemoryUsed();
 
   PRUint32 styleSize;
   styleSize = aShell->StyleSet()->SizeOf();
 
+  PRUint64 textRunsSize;
+  textRunsSize = aShell->ComputeTextRunMemoryUsed();
+
   data->callback->
     Callback(EmptyCString(), arenaPath, nsIMemoryReporter::KIND_HEAP,
              nsIMemoryReporter::UNITS_BYTES, arenasSize, kArenaDesc,
              data->closure);
 
   data->callback->
     Callback(EmptyCString(), stylePath, nsIMemoryReporter::KIND_HEAP,
              nsIMemoryReporter::UNITS_BYTES, styleSize, kStyleDesc,
              data->closure);
 
+  if (textRunsSize) {
+    data->callback->
+      Callback(EmptyCString(), textRunsPath, nsIMemoryReporter::KIND_HEAP,
+               nsIMemoryReporter::UNITS_BYTES, textRunsSize, kTextRunsDesc,
+               data->closure);
+  }
+
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 PresShell::MemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
                                           nsISupports* aClosure)
 {
   MemoryReporterData data;
   data.callback = aCb;
   data.closure = aClosure;
 
+  // clear TEXT_RUN_SIZE_ACCOUNTED flag on cached runs
+  gfxTextRunWordCache::ComputeStorage(nsnull);
+
   sLiveShells->EnumerateEntries(SizeEnumerator, &data);
 
+  NS_NAMED_LITERAL_CSTRING(kTextRunWordCachePath,
+                           "explicit/gfx/textrun-word-cache");
+  NS_NAMED_LITERAL_CSTRING(kTextRunWordCacheDesc,
+                           "Memory used by cached text-runs that are "
+                           "not owned by a PresShell's frame tree.");
+
+  // now total up cached runs that aren't otherwise accounted for
+  PRUint64 textRunWordCacheSize = 0;
+  gfxTextRunWordCache::ComputeStorage(&textRunWordCacheSize);
+
+  aCb->Callback(EmptyCString(), kTextRunWordCachePath,
+                nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
+                textRunWordCacheSize, kTextRunWordCacheDesc, aClosure);
+
   return NS_OK;
 }
 
 class nsAutoCauseReflowNotifier
 {
 public:
   nsAutoCauseReflowNotifier(PresShell* aShell)
     : mShell(aShell)
@@ -8705,8 +8736,27 @@ PresShell::GetRootPresShell()
   if (mPresContext) {
     nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
     if (rootPresContext) {
       return static_cast<PresShell*>(rootPresContext->PresShell());
     }
   }
   return nsnull;
 }
+
+PRUint64
+PresShell::ComputeTextRunMemoryUsed()
+{
+  nsIFrame* rootFrame = FrameManager()->GetRootFrame();
+  if (!rootFrame) {
+    return 0;
+  }
+
+  // clear the TEXT_RUN_MEMORY_ACCOUNTED flags
+  nsLayoutUtils::GetTextRunMemoryForFrames(rootFrame, nsnull);
+
+  // collect the total memory in use for textruns
+  PRUint64 total = 0;
+  nsLayoutUtils::GetTextRunMemoryForFrames(rootFrame, &total);
+
+  return total;
+}
+
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -893,16 +893,18 @@ public:
 
     result += sizeof(PresShell);
     result += mStackArena.Size();
     result += mFrameArena.Size();
 
     return result;
   }
 
+  PRUint64 ComputeTextRunMemoryUsed();
+
   class MemoryReporter : public nsIMemoryMultiReporter
   {
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIMEMORYMULTIREPORTER
   protected:
     static PLDHashOperator SizeEnumerator(PresShellPtrKey *aEntry, void *userArg);
   };
--- a/layout/generic/nsTextRunTransformations.cpp
+++ b/layout/generic/nsTextRunTransformations.cpp
@@ -94,16 +94,38 @@ nsTransformedTextRun::SetPotentialLineBr
   bool changed = gfxTextRun::SetPotentialLineBreaks(aStart, aLength,
       aBreakBefore, aRefContext);
   if (changed) {
     mNeedsRebuild = PR_TRUE;
   }
   return changed;
 }
 
+PRUint64
+nsTransformedTextRun::ComputeSize()
+{
+  PRUint32 total = gfxTextRun::ComputeSize();
+  if (moz_malloc_usable_size(this) == 0) {
+    total += sizeof(nsTransformedTextRun) - sizeof(gfxTextRun);
+  }
+  total += mStyles.SizeOf();
+  total += mCapitalize.SizeOf();
+  if (mOwnsFactory) {
+    PRUint32 factorySize = moz_malloc_usable_size(mFactory);
+    if (factorySize == 0) {
+      // this may not quite account for everything
+      // (e.g. nsCaseTransformTextRunFactory adds a couple of members)
+      // but I'm not sure it's worth the effort to track more precisely
+      factorySize = sizeof(nsTransformingTextRunFactory);
+    }
+    total += factorySize;
+  }
+  return total;
+}
+
 nsTransformedTextRun*
 nsTransformingTextRunFactory::MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
                                           const gfxTextRunFactory::Parameters* aParams,
                                           gfxFontGroup* aFontGroup, PRUint32 aFlags,
                                           nsStyleContext** aStyles, bool aOwnsFactory)
 {
   return nsTransformedTextRun::Create(aParams, this, aFontGroup,
                                       aString, aLength, aFlags, aStyles, aOwnsFactory);
--- a/layout/generic/nsTextRunTransformations.h
+++ b/layout/generic/nsTextRunTransformations.h
@@ -127,16 +127,19 @@ public:
   void FinishSettingProperties(gfxContext* aRefContext)
   {
     if (mNeedsRebuild) {
       mNeedsRebuild = PR_FALSE;
       mFactory->RebuildTextRun(this, aRefContext);
     }
   }
 
+  // override the gfxTextRun impl to account for additional members here
+  virtual PRUint64 ComputeSize();
+
   nsTransformingTextRunFactory       *mFactory;
   nsTArray<nsRefPtr<nsStyleContext> > mStyles;
   nsTArray<bool>              mCapitalize;
   bool                                mOwnsFactory;
   bool                                mNeedsRebuild;
 
 private:
   nsTransformedTextRun(const gfxTextRunFactory::Parameters* aParams,
--- a/xpcom/glue/nsTArray-inl.h
+++ b/xpcom/glue/nsTArray-inl.h
@@ -50,43 +50,43 @@ template<class Alloc>
 nsTArray_base<Alloc>::~nsTArray_base() {
   if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) {
     Alloc::Free(mHdr);
   }
   MOZ_COUNT_DTOR(nsTArray_base);
 }
 
 template<class Alloc>
-nsTArrayHeader* nsTArray_base<Alloc>::GetAutoArrayBufferUnsafe(size_t elemAlign) {
+const nsTArrayHeader* nsTArray_base<Alloc>::GetAutoArrayBufferUnsafe(size_t elemAlign) const {
   // Assuming |this| points to an nsAutoArray, we want to get a pointer to
   // mAutoBuf.  So just cast |this| to nsAutoArray* and read &mAutoBuf!
 
-  void* autoBuf = &reinterpret_cast<nsAutoArrayBase<nsTArray<PRUint32>, 1>*>(this)->mAutoBuf;
+  const void* autoBuf = &reinterpret_cast<const nsAutoArrayBase<nsTArray<PRUint32>, 1>*>(this)->mAutoBuf;
 
   // If we're on a 32-bit system and elemAlign is 8, we need to adjust our
   // pointer to take into account the extra alignment in the auto array.
 
   // Check that the auto array is padded as we expect.
   PR_STATIC_ASSERT(sizeof(void*) != 4 ||
                    (MOZ_ALIGNOF(mozilla::AlignedElem<8>) == 8 &&
                     sizeof(nsAutoTArray<mozilla::AlignedElem<8>, 1>) ==
                       sizeof(void*) + sizeof(nsTArrayHeader) +
                       4 + sizeof(mozilla::AlignedElem<8>)));
 
   // We don't support alignments greater than 8 bytes.
   NS_ABORT_IF_FALSE(elemAlign <= 4 || elemAlign == 8, "unsupported alignment.");
   if (sizeof(void*) == 4 && elemAlign == 8) {
-    autoBuf = reinterpret_cast<char*>(autoBuf) + 4;
+    autoBuf = reinterpret_cast<const char*>(autoBuf) + 4;
   }
 
-  return reinterpret_cast<Header*>(autoBuf);
+  return reinterpret_cast<const Header*>(autoBuf);
 }
 
 template<class Alloc>
-bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() {
+bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() const {
   if (!mHdr->mIsAutoArray) {
     return PR_FALSE;
   }
 
   // This is nuts.  If we were sane, we'd pass elemAlign as a parameter to
   // this function.  Unfortunately this function is called in nsTArray_base's
   // destructor, at which point we don't know elem_type's alignment.
   //
@@ -113,18 +113,18 @@ bool nsTArray_base<Alloc>::UsesAutoArray
   // Note that this means that we can't store elements with alignment 16 in an
   // nsTArray, because GetAutoArrayBuffer(16) could lie outside the memory
   // owned by this nsAutoTArray.  We statically assert that elem_type's
   // alignment is 8 bytes or less in nsAutoArrayBase.
 
   PR_STATIC_ASSERT(sizeof(nsTArrayHeader) > 4);
 
 #ifdef DEBUG
-  PRPtrdiff diff = reinterpret_cast<char*>(GetAutoArrayBuffer(8)) -
-                   reinterpret_cast<char*>(GetAutoArrayBuffer(4));
+  PRPtrdiff diff = reinterpret_cast<const char*>(GetAutoArrayBuffer(8)) -
+                   reinterpret_cast<const char*>(GetAutoArrayBuffer(4));
   NS_ABORT_IF_FALSE(diff >= 0 && diff <= 4, "GetAutoArrayBuffer doesn't do what we expect.");
 #endif
 
   return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8);
 }
 
 
 template<class Alloc>
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -251,33 +251,41 @@ protected:
       bool mIsAuto;
   };
 
   // Helper function for SwapArrayElements. Ensures that if the array
   // is an nsAutoTArray that it doesn't use the built-in buffer.
   bool EnsureNotUsingAutoArrayBuffer(size_type elemSize);
 
   // Returns true if this nsTArray is an nsAutoTArray with a built-in buffer.
-  bool IsAutoArray() {
+  bool IsAutoArray() const {
     return mHdr->mIsAutoArray;
   }
 
   // Returns a Header for the built-in buffer of this nsAutoTArray.
   Header* GetAutoArrayBuffer(size_t elemAlign) {
     NS_ASSERTION(IsAutoArray(), "Should be an auto array to call this");
     return GetAutoArrayBufferUnsafe(elemAlign);
   }
+  const Header* GetAutoArrayBuffer(size_t elemAlign) const {
+    NS_ASSERTION(IsAutoArray(), "Should be an auto array to call this");
+    return GetAutoArrayBufferUnsafe(elemAlign);
+  }
 
   // Returns a Header for the built-in buffer of this nsAutoTArray, but doesn't
   // assert that we are an nsAutoTArray.
-  Header* GetAutoArrayBufferUnsafe(size_t elemAlign);
+  Header* GetAutoArrayBufferUnsafe(size_t elemAlign) {
+    return const_cast<Header*>(static_cast<const nsTArray_base<Alloc>*>(this)->
+                               GetAutoArrayBufferUnsafe(elemAlign));
+  }
+  const Header* GetAutoArrayBufferUnsafe(size_t elemAlign) const;
 
   // Returns true if this is an nsAutoTArray and it currently uses the
   // built-in buffer to store its elements.
-  bool UsesAutoArrayBuffer();
+  bool UsesAutoArrayBuffer() const;
 
   // The array's elements (prefixed with a Header).  This pointer is never
   // null.  If the array is empty, then this will point to sEmptyHdr.
   Header *mHdr;
 
   Header* Hdr() const { 
     return mHdr;
   }
@@ -470,17 +478,18 @@ public:
   nsTArray& operator=(const nsTArray<E, Allocator>& other) {
     ReplaceElementsAt(0, Length(), other.Elements(), other.Length());
     return *this;
   }
 
   // @return The amount of memory taken used by this nsTArray, not including
   // sizeof(this)
   size_t SizeOf() const {
-    return this->Capacity() * sizeof(elem_type) + sizeof(*this->Hdr());
+    return this->UsesAutoArrayBuffer() ?
+      0 : this->Capacity() * sizeof(elem_type) + sizeof(*this->Hdr());
   }
 
   //
   // Accessor methods
   //
 
   // This method provides direct access to the array elements.
   // @return A pointer to the first element of the array.  If the array is