Bug 664659 - Fix about:memory image reporters. r=joe,njn
authorJustin Lebar <justin.lebar@gmail.com>
Mon, 18 Jul 2011 09:20:27 -0400
changeset 73407 7b49a2857e18220b0b576fb06865651abae0e6f3
parent 73406 5ea415e7f9a6836bcfb67e2191593ce356ad3acc
child 73408 e3d1cabfaf4c2e945e2393883b1d6985674ac3f1
push idunknown
push userunknown
push dateunknown
reviewersjoe, njn
bugs664659
milestone8.0a1
Bug 664659 - Fix about:memory image reporters. r=joe,njn
gfx/thebes/gfxASurface.cpp
gfx/thebes/gfxASurface.h
gfx/thebes/gfxWindowsSurface.h
gfx/thebes/gfxXlibSurface.cpp
gfx/thebes/gfxXlibSurface.h
modules/libpr0n/src/Image.cpp
modules/libpr0n/src/Image.h
modules/libpr0n/src/RasterImage.cpp
modules/libpr0n/src/RasterImage.h
modules/libpr0n/src/VectorImage.cpp
modules/libpr0n/src/VectorImage.h
modules/libpr0n/src/imgFrame.cpp
modules/libpr0n/src/imgFrame.h
modules/libpr0n/src/imgLoader.cpp
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -537,58 +537,83 @@ gfxASurface::MovePixels(const nsIntRect&
                            aDestTopLeft.y, 
                            aSourceRect.width, 
                            aSourceRect.height));
     ctx->Fill();
 }
 
 /** Memory reporting **/
 
-static const char *sSurfaceNamesForSurfaceType[] = {
-    "gfx-surface-image",
-    "gfx-surface-pdf",
-    "gfx-surface-ps",
-    "gfx-surface-xlib",
-    "gfx-surface-xcb",
-    "gfx-surface-glitz",
-    "gfx-surface-quartz",
-    "gfx-surface-win32",
-    "gfx-surface-beos",
-    "gfx-surface-directfb",
-    "gfx-surface-svg",
-    "gfx-surface-os2",
-    "gfx-surface-win32printing",
-    "gfx-surface-quartzimage",
-    "gfx-surface-script",
-    "gfx-surface-qpainter",
-    "gfx-surface-recording",
-    "gfx-surface-vg",
-    "gfx-surface-gl",
-    "gfx-surface-drm",
-    "gfx-surface-tee",
-    "gfx-surface-xml",
-    "gfx-surface-skia",
-    "gfx-surface-subsurface",
-    "gfx-surface-d2d"
+static const char *sDefaultSurfaceDescription =
+    "Memory used by gfx surface of the given type.";
+
+struct SurfaceMemoryReporterAttrs {
+  const char *name;
+  const char *description;
 };
 
-PR_STATIC_ASSERT(NS_ARRAY_LENGTH(sSurfaceNamesForSurfaceType) == gfxASurface::SurfaceTypeMax);
+static SurfaceMemoryReporterAttrs sSurfaceMemoryReporterAttrs[] = {
+    {"gfx-surface-image", nsnull},
+    {"gfx-surface-pdf", nsnull},
+    {"gfx-surface-ps", nsnull},
+    {"gfx-surface-xlib",
+     "Memory used by xlib surfaces to store pixmaps. This memory lives in "
+     "the X server's process rather than in this application, so the bytes "
+     "accounted for here aren't counted in vsize, resident, explicit, or any of "
+     "the other measurements on this page."},
+    {"gfx-surface-xcb", nsnull},
+    {"gfx-surface-glitz", nsnull},
+    {"gfx-surface-quartz", nsnull},
+    {"gfx-surface-win32", nsnull},
+    {"gfx-surface-beos", nsnull},
+    {"gfx-surface-directfb", nsnull},
+    {"gfx-surface-svg", nsnull},
+    {"gfx-surface-os2", nsnull},
+    {"gfx-surface-win32printing", nsnull},
+    {"gfx-surface-quartzimage", nsnull},
+    {"gfx-surface-script", nsnull},
+    {"gfx-surface-qpainter", nsnull},
+    {"gfx-surface-recording", nsnull},
+    {"gfx-surface-vg", nsnull},
+    {"gfx-surface-gl", nsnull},
+    {"gfx-surface-drm", nsnull},
+    {"gfx-surface-tee", nsnull},
+    {"gfx-surface-xml", nsnull},
+    {"gfx-surface-skia", nsnull},
+    {"gfx-surface-subsurface", nsnull},
+    {"gfx-surface-d2d", nsnull},
+};
+
+PR_STATIC_ASSERT(NS_ARRAY_LENGTH(sSurfaceMemoryReporterAttrs) ==
+                 gfxASurface::SurfaceTypeMax);
 #ifdef CAIRO_HAS_D2D_SURFACE
-PR_STATIC_ASSERT(PRUint32(CAIRO_SURFACE_TYPE_D2D) == PRUint32(gfxASurface::SurfaceTypeD2D));
+PR_STATIC_ASSERT(PRUint32(CAIRO_SURFACE_TYPE_D2D) ==
+                 PRUint32(gfxASurface::SurfaceTypeD2D));
 #endif
-PR_STATIC_ASSERT(PRUint32(CAIRO_SURFACE_TYPE_SKIA) == PRUint32(gfxASurface::SurfaceTypeSkia));
+PR_STATIC_ASSERT(PRUint32(CAIRO_SURFACE_TYPE_SKIA) ==
+                 PRUint32(gfxASurface::SurfaceTypeSkia));
 
 static const char *
 SurfaceMemoryReporterPathForType(gfxASurface::gfxSurfaceType aType)
 {
     if (aType < 0 ||
         aType >= gfxASurface::SurfaceTypeMax)
         return "gfx-surface-unknown";
 
-    return sSurfaceNamesForSurfaceType[aType];
+    return sSurfaceMemoryReporterAttrs[aType].name;
+}
+
+static const char *
+SurfaceMemoryReporterDescriptionForType(gfxASurface::gfxSurfaceType aType)
+{
+    if (aType >= 0 && aType < gfxASurface::SurfaceTypeMax &&
+        sSurfaceMemoryReporterAttrs[aType].description)
+        return sSurfaceMemoryReporterAttrs[aType].description;
+
+    return sDefaultSurfaceDescription;
 }
 
 /* Surface size memory reporting */
 static nsIMemoryReporter *gSurfaceMemoryReporters[gfxASurface::SurfaceTypeMax] = { 0 };
 static PRInt64 gSurfaceMemoryUsed[gfxASurface::SurfaceTypeMax] = { 0 };
 
 class SurfaceMemoryReporter :
     public nsIMemoryReporter
@@ -621,17 +646,17 @@ public:
     }
 
     NS_IMETHOD GetAmount(PRInt64 *amount) {
         *amount = gSurfaceMemoryUsed[mType];
         return NS_OK;
     }
 
     NS_IMETHOD GetDescription(nsACString &desc) {
-        desc.AssignLiteral("Memory used by gfx surface of the given type.");
+        desc.Assign(SurfaceMemoryReporterDescriptionForType(mType));
         return NS_OK;
     }
 
     gfxASurface::gfxSurfaceType mType;
 };
 
 NS_IMPL_ISUPPORTS1(SurfaceMemoryReporter, nsIMemoryReporter)
 
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -203,16 +203,33 @@ public:
      * in which case the value that was recorded for this surface will
      * be freed.
      */
     void RecordMemoryUsed(PRInt32 aBytes);
     void RecordMemoryFreed();
 
     PRInt32 KnownMemoryUsed() { return mBytesRecorded; }
 
+    /**
+     * The memory used by this surface (as reported by KnownMemoryUsed()) can
+     * either live in this process's heap, in this process but outside the
+     * heap, or in another process altogether.
+     */
+    enum MemoryLocation {
+      MEMORY_IN_PROCESS_HEAP,
+      MEMORY_IN_PROCESS_NONHEAP,
+      MEMORY_OUT_OF_PROCESS
+    };
+
+    /**
+     * Where does this surface's memory live?  By default, say it's in this
+     * process's heap.
+     */
+    virtual MemoryLocation GetMemoryLocation() { return MEMORY_IN_PROCESS_HEAP; }
+
     static PRInt32 BytePerPixelFromFormat(gfxImageFormat format);
 
     virtual const gfxIntSize GetSize() const { return gfxIntSize(-1, -1); }
 
     void DumpAsDataURL();
 
     void SetOpaqueRect(const gfxRect& aRect) {
         if (aRect.IsEmpty()) {
--- a/gfx/thebes/gfxWindowsSurface.h
+++ b/gfx/thebes/gfxWindowsSurface.h
@@ -93,16 +93,20 @@ public:
     virtual PRInt32 GetDefaultContextFlags() const;
 
     void MovePixels(const nsIntRect& aSourceRect,
                     const nsIntPoint& aDestTopLeft)
     {
         FastMovePixels(aSourceRect, aDestTopLeft);
     }
 
+    // The memory used by this surface lives in this process's address space,
+    // but not in the heap.
+    virtual MemoryLocation MemoryLocation() { return MEMORY_IN_PROCESS_NONHEAP; }
+
 private:
     PRPackedBool mOwnsDC;
     PRPackedBool mForPrinting;
 
     HDC mDC;
     HWND mWnd;
 };
 
--- a/gfx/thebes/gfxXlibSurface.cpp
+++ b/gfx/thebes/gfxXlibSurface.cpp
@@ -99,16 +99,17 @@ gfxXlibSurface::gfxXlibSurface(cairo_sur
     mDrawable = cairo_xlib_surface_get_drawable(csurf);
     mDisplay = cairo_xlib_surface_get_display(csurf);
 
     Init(csurf, PR_TRUE);
 }
 
 gfxXlibSurface::~gfxXlibSurface()
 {
+    // gfxASurface's destructor calls RecordMemoryFreed().
     if (mPixmapTaken) {
         XFreePixmap (mDisplay, mDrawable);
     }
 }
 
 static Drawable
 CreatePixmap(Screen *screen, const gfxIntSize& size, unsigned int depth,
              Drawable relatedDrawable)
@@ -122,16 +123,36 @@ CreatePixmap(Screen *screen, const gfxIn
     Display *dpy = DisplayOfScreen(screen);
     // X gives us a fatal error if we try to create a pixmap of width
     // or height 0
     return XCreatePixmap(dpy, relatedDrawable,
                          NS_MAX(1, size.width), NS_MAX(1, size.height),
                          depth);
 }
 
+void
+gfxXlibSurface::TakePixmap()
+{
+    NS_ASSERTION(!mPixmapTaken, "I already own the Pixmap!");
+    mPixmapTaken = PR_TRUE;
+
+    // Divide by 8 because surface_get_depth gives us the number of *bits* per
+    // pixel.
+    RecordMemoryUsed(mSize.width * mSize.height *
+        cairo_xlib_surface_get_depth(CairoSurface()) / 8);
+}
+
+Drawable
+gfxXlibSurface::ReleasePixmap() {
+    NS_ASSERTION(mPixmapTaken, "I don't own the Pixmap!");
+    mPixmapTaken = PR_FALSE;
+    RecordMemoryFreed();
+    return mDrawable;
+}
+
 /* static */
 already_AddRefed<gfxXlibSurface>
 gfxXlibSurface::Create(Screen *screen, Visual *visual,
                        const gfxIntSize& size, Drawable relatedDrawable)
 {
     Drawable drawable =
         CreatePixmap(screen, size, DepthOfVisual(screen, visual),
                      relatedDrawable);
--- a/gfx/thebes/gfxXlibSurface.h
+++ b/gfx/thebes/gfxXlibSurface.h
@@ -85,33 +85,33 @@ public:
     XRenderPictFormat* XRenderFormat();
 
     static int DepthOfVisual(const Screen* screen, const Visual* visual);
     static Visual* FindVisual(Screen* screen, gfxImageFormat format);
     static XRenderPictFormat *FindRenderFormat(Display *dpy, gfxImageFormat format);
 
     // take ownership of a passed-in Pixmap, calling XFreePixmap on it
     // when the gfxXlibSurface is destroyed.
-    void TakePixmap() {
-        NS_ASSERTION(!mPixmapTaken, "I already own the Pixmap!");
-        mPixmapTaken = PR_TRUE;
-    }
+    void TakePixmap();
 
     // Release ownership of this surface's Pixmap.  This is only valid
     // on gfxXlibSurfaces for which the user called TakePixmap(), or
     // on those created by a Create() factory method.
-    Drawable ReleasePixmap() {
-        NS_ASSERTION(mPixmapTaken, "I don't own the Pixmap!");
-        mPixmapTaken = PR_FALSE;
-        return mDrawable;
-    }
+    Drawable ReleasePixmap();
 
     // Find a visual and colormap pair suitable for rendering to this surface.
     PRBool GetColormapAndVisual(Colormap* colormap, Visual **visual);
 
+    // This surface is a wrapper around X pixmaps, which are stored in the X
+    // server, not the main application.
+    virtual gfxASurface::MemoryLocation MemoryLocation()
+    {
+      return MEMORY_OUT_OF_PROCESS;
+    }
+
 protected:
     // if TakePixmap() has been called on this
     PRBool mPixmapTaken;
     
     Display *mDisplay;
     Drawable mDrawable;
 
     void DoSizeQuery();
--- a/modules/libpr0n/src/Image.cpp
+++ b/modules/libpr0n/src/Image.cpp
@@ -58,17 +58,17 @@ Image::Image(imgStatusTracker* aStatusTr
 }
 
 PRUint32
 Image::GetDataSize()
 {
   if (mError)
     return 0;
   
-  return GetSourceDataSize() + GetDecodedDataSize();
+  return GetSourceHeapSize() + GetDecodedHeapSize() + GetDecodedNonheapSize();
 }
 
 // Translates a mimetype into a concrete decoder
 Image::eDecoderType
 Image::GetDecoderType(const char *aMimeType)
 {
   // By default we don't know
   eDecoderType rv = eDecoderType_unknown;
--- a/modules/libpr0n/src/Image.h
+++ b/modules/libpr0n/src/Image.h
@@ -97,18 +97,19 @@ public:
    * The size, in bytes, occupied by the significant data portions of the image.
    * This includes both compressed source data and decoded frames.
    */
   PRUint32 GetDataSize();
 
   /**
    * The components that make up GetDataSize().
    */      
-  virtual PRUint32 GetDecodedDataSize() = 0;
-  virtual PRUint32 GetSourceDataSize() = 0;
+  virtual PRUint32 GetDecodedHeapSize() = 0;
+  virtual PRUint32 GetDecodedNonheapSize() = 0;
+  virtual PRUint32 GetSourceHeapSize() = 0;
 
   // Mimetype translation
   enum eDecoderType {
     eDecoderType_png     = 0,
     eDecoderType_gif     = 1,
     eDecoderType_jpeg    = 2,
     eDecoderType_bmp     = 3,
     eDecoderType_ico     = 4,
--- a/modules/libpr0n/src/RasterImage.cpp
+++ b/modules/libpr0n/src/RasterImage.cpp
@@ -733,31 +733,55 @@ RasterImage::GetFrame(PRUint32 aWhichFra
     framesurf = imgsurf;
   }
 
   *_retval = framesurf.forget().get();
 
   return rv;
 }
 
+namespace {
+
+enum DecodedSizeType {
+  DECODED_SIZE_HEAP,
+  DECODED_SIZE_NONHEAP
+};
+
 PRUint32
-RasterImage::GetDecodedDataSize()
+GetDecodedSize(const nsTArray<imgFrame *> &aFrames, DecodedSizeType aType)
 {
   PRUint32 val = 0;
-  for (PRUint32 i = 0; i < mFrames.Length(); ++i) {
-    imgFrame *frame = mFrames.SafeElementAt(i, nsnull);
+  for (PRUint32 i = 0; i < aFrames.Length(); ++i) {
+    imgFrame *frame = aFrames.SafeElementAt(i, nsnull);
     NS_ABORT_IF_FALSE(frame, "Null frame in frame array!");
-    val += frame->EstimateMemoryUsed();
+    if (aType == DECODED_SIZE_HEAP)
+      val += frame->EstimateHeapMemoryUsed();
+    else
+      val += frame->EstimateNonheapMemoryUsed();
   }
 
   return val;
 }
 
+} // anonymous namespace
+
 PRUint32
-RasterImage::GetSourceDataSize()
+RasterImage::GetDecodedNonheapSize()
+{
+  return GetDecodedSize(mFrames, DECODED_SIZE_NONHEAP);
+}
+
+PRUint32
+RasterImage::GetDecodedHeapSize()
+{
+  return GetDecodedSize(mFrames, DECODED_SIZE_HEAP);
+}
+
+PRUint32
+RasterImage::GetSourceHeapSize()
 {
   PRUint32 sourceDataSize = mSourceData.Length();
   
   NS_ABORT_IF_FALSE(StoringSourceData() || (sourceDataSize == 0),
                     "Non-zero source data size when we aren't storing it?");
   return sourceDataSize;
 }
 
--- a/modules/libpr0n/src/RasterImage.h
+++ b/modules/libpr0n/src/RasterImage.h
@@ -204,18 +204,19 @@ public:
 
   /* The index of the current frame that would be drawn if the image was to be
    * drawn now. */
   PRUint32 GetCurrentFrameIndex();
 
   /* The total number of frames in this image. */
   PRUint32 GetNumFrames();
 
-  PRUint32 GetDecodedDataSize();
-  PRUint32 GetSourceDataSize();
+  virtual PRUint32 GetDecodedHeapSize();
+  virtual PRUint32 GetDecodedNonheapSize();
+  virtual PRUint32 GetSourceHeapSize();
 
   /* Triggers discarding. */
   void Discard(bool force = false);
   void ForceDiscard() { Discard(/* force = */ true); }
 
   /* Callbacks for decoders */
   nsresult SetFrameDisposalMethod(PRUint32 aFrameNum,
                                   PRInt32 aDisposalMethod);
--- a/modules/libpr0n/src/VectorImage.cpp
+++ b/modules/libpr0n/src/VectorImage.cpp
@@ -244,24 +244,30 @@ VectorImage::Init(imgIDecoderObserver* a
 
 void
 VectorImage::GetCurrentFrameRect(nsIntRect& aRect)
 {
   aRect = nsIntRect::GetMaxSizedIntRect();
 }
 
 PRUint32
-VectorImage::GetDecodedDataSize()
+VectorImage::GetDecodedHeapSize()
 {
   // XXXdholbert TODO: return num bytes used by helper SVG doc. (bug 590790)
   return sizeof(*this);
 }
 
 PRUint32
-VectorImage::GetSourceDataSize()
+VectorImage::GetDecodedNonheapSize()
+{
+  return 0;
+}
+
+PRUint32
+VectorImage::GetSourceHeapSize()
 {
   // We're not storing the source data -- we just feed that directly to
   // our helper SVG document as we receive it, for it to parse.
   // So 0 is an appropriate return value here.
   return 0;
 }
 
 nsresult
--- a/modules/libpr0n/src/VectorImage.h
+++ b/modules/libpr0n/src/VectorImage.h
@@ -82,18 +82,20 @@ public:
   virtual ~VectorImage();
 
   // Methods inherited from Image
   nsresult Init(imgIDecoderObserver* aObserver,
                 const char* aMimeType,
                 const char* aURIString,
                 PRUint32 aFlags);
   void GetCurrentFrameRect(nsIntRect& aRect);
-  PRUint32 GetDecodedDataSize();
-  PRUint32 GetSourceDataSize();
+
+  virtual PRUint32 GetDecodedHeapSize();
+  virtual PRUint32 GetDecodedNonheapSize();
+  virtual PRUint32 GetSourceHeapSize();
 
   // Callback for SVGRootRenderingObserver
   void InvalidateObserver();
 
 protected:
   virtual nsresult StartAnimation();
   virtual nsresult StopAnimation();
   virtual PRBool   ShouldAnimate();
--- a/modules/libpr0n/src/imgFrame.cpp
+++ b/modules/libpr0n/src/imgFrame.cpp
@@ -773,45 +773,66 @@ PRBool imgFrame::GetCompositingFailed() 
   return mCompositingFailed;
 }
 
 void imgFrame::SetCompositingFailed(PRBool val)
 {
   mCompositingFailed = val;
 }
 
-PRUint32 imgFrame::EstimateMemoryUsed() const
+PRUint32 imgFrame::EstimateHeapMemoryUsed() const
 {
   PRUint32 size = 0;
 
   if (mSinglePixel) {
     size += sizeof(gfxRGBA);
   }
 
   if (mPalettedImageData) {
     size += GetImageDataLength() + PaletteDataLength();
   }
 
 #ifdef USE_WIN_SURFACE
-  if (mWinSurface) {
+  if (mWinSurface && mWinSurface->GetMemoryLocation() ==
+                     gfxASurface::MEMORY_IN_PROCESS_HEAP) {
     size += mWinSurface->KnownMemoryUsed();
   } else
 #endif
 #ifdef XP_MACOSX
   if (mQuartzSurface) {
     size += mSize.width * mSize.height * 4;
   } else
 #endif
-  if (mImageSurface) {
+  if (mImageSurface && mImageSurface->GetMemoryLocation() ==
+                       gfxASurface::MEMORY_IN_PROCESS_HEAP) {
     size += mImageSurface->KnownMemoryUsed();
   }
 
-  if (mOptSurface) {
+  if (mOptSurface && mOptSurface->GetMemoryLocation() ==
+                     gfxASurface::MEMORY_IN_PROCESS_HEAP) {
     size += mOptSurface->KnownMemoryUsed();
   }
 
-  // fall back to pessimistic/approximate size
-  if (size == 0) {
-    size = mSize.width * mSize.height * 4;
-  }
-
   return size;
 }
+
+PRUint32 imgFrame::EstimateNonheapMemoryUsed() const
+{
+  PRUint32 size = 0;
+
+#ifdef USE_WIN_SURFACE
+  if (mWinSurface && mWinSurface->GetMemoryLocation() ==
+                     gfxASurface::MEMORY_IN_PROCESS_NONHEAP) {
+    size += mWinSurface->KnownMemoryUsed();
+  } else
+#endif
+  if (mImageSurface && mImageSurface->GetMemoryLocation() ==
+                       gfxASurface::MEMORY_IN_PROCESS_NONHEAP) {
+    size += mImageSurface->KnownMemoryUsed();
+  }
+
+  if (mOptSurface && mOptSurface->GetMemoryLocation() ==
+                     gfxASurface::MEMORY_IN_PROCESS_NONHEAP) {
+    size += mOptSurface->KnownMemoryUsed();
+  }
+
+  return size;
+}
--- a/modules/libpr0n/src/imgFrame.h
+++ b/modules/libpr0n/src/imgFrame.h
@@ -130,18 +130,22 @@ public:
       return mWinSurface;
 #elif defined(XP_MACOSX)
     if (mQuartzSurface)
       return mQuartzSurface;
 #endif
     return mImageSurface;
   }
 
-  // returns an estimate of the memory used by this imgFrame
-  PRUint32 EstimateMemoryUsed() const;
+  // These functions estimate how much heap and non-heap memory the imgFrame is
+  // using.  Note that these only report memory within the current process; an
+  // imgFrame's surface might be using memory outside the current process (e.g.
+  // gfxXlibSurface).
+  PRUint32 EstimateHeapMemoryUsed() const;
+  PRUint32 EstimateNonheapMemoryUsed() const;
 
   PRUint8 GetPaletteDepth() const { return mPaletteDepth; }
 
 private: // methods
   PRUint32 PaletteDataLength() const {
     return ((1 << mPaletteDepth) * sizeof(PRUint32));
   }
 
--- a/modules/libpr0n/src/imgLoader.cpp
+++ b/modules/libpr0n/src/imgLoader.cpp
@@ -138,64 +138,87 @@ static void PrintImageDecoders()
 class imgMemoryReporter :
   public nsIMemoryReporter
 {
 public:
   enum ReporterType {
     CHROME_BIT = PR_BIT(0),
     USED_BIT   = PR_BIT(1),
     RAW_BIT    = PR_BIT(2),
+    HEAP_BIT   = PR_BIT(3),
 
-    ChromeUsedRaw             = CHROME_BIT | USED_BIT | RAW_BIT,
-    ChromeUsedUncompressed    = CHROME_BIT | USED_BIT,
-    ChromeUnusedRaw           = CHROME_BIT | RAW_BIT,
-    ChromeUnusedUncompressed  = CHROME_BIT,
-    ContentUsedRaw            = USED_BIT | RAW_BIT,
-    ContentUsedUncompressed   = USED_BIT,
-    ContentUnusedRaw          = RAW_BIT,
-    ContentUnusedUncompressed = 0
+    ChromeUsedRaw                     = CHROME_BIT | USED_BIT | RAW_BIT | HEAP_BIT,
+    ChromeUsedUncompressedHeap        = CHROME_BIT | USED_BIT | HEAP_BIT,
+    ChromeUsedUncompressedNonheap     = CHROME_BIT | USED_BIT,
+    ChromeUnusedRaw                   = CHROME_BIT | RAW_BIT | HEAP_BIT,
+    ChromeUnusedUncompressedHeap      = CHROME_BIT | HEAP_BIT,
+    ChromeUnusedUncompressedNonheap   = CHROME_BIT,
+    ContentUsedRaw                    = USED_BIT | RAW_BIT | HEAP_BIT,
+    ContentUsedUncompressedHeap       = USED_BIT | HEAP_BIT,
+    ContentUsedUncompressedNonheap    = USED_BIT,
+    ContentUnusedRaw                  = RAW_BIT | HEAP_BIT,
+    ContentUnusedUncompressedHeap     = HEAP_BIT,
+    ContentUnusedUncompressedNonheap  = 0
   };
 
   imgMemoryReporter(ReporterType aType)
     : mType(aType)
-  { }
+  {
+    // If the RAW bit is set, HEAP should also be set, because we don't
+    // currently understand storing compressed image data off the heap.
+    NS_ASSERTION(!(aType & RAW_BIT) || (aType & HEAP_BIT),
+                 "RAW bit should imply HEAP bit.");
+  }
 
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD GetProcess(nsACString &process)
   {
     process.Truncate();
     return NS_OK;
   }
 
   NS_IMETHOD GetPath(nsACString &path)
   {
     if (mType == ChromeUsedRaw) {
       path.AssignLiteral("explicit/images/chrome/used/raw");
-    } else if (mType == ChromeUsedUncompressed) {
-      path.AssignLiteral("explicit/images/chrome/used/uncompressed");
+    } else if (mType == ChromeUsedUncompressedHeap) {
+      path.AssignLiteral("explicit/images/chrome/used/uncompressed-heap");
+    } else if (mType == ChromeUsedUncompressedNonheap) {
+      path.AssignLiteral("explicit/images/chrome/used/uncompressed-nonheap");
     } else if (mType == ChromeUnusedRaw) {
       path.AssignLiteral("explicit/images/chrome/unused/raw");
-    } else if (mType == ChromeUnusedUncompressed) {
-      path.AssignLiteral("explicit/images/chrome/unused/uncompressed");
+    } else if (mType == ChromeUnusedUncompressedHeap) {
+      path.AssignLiteral("explicit/images/chrome/unused/uncompressed-heap");
+    } else if (mType == ChromeUnusedUncompressedNonheap) {
+      path.AssignLiteral("explicit/images/chrome/unused/uncompressed-nonheap");
     } else if (mType == ContentUsedRaw) {
       path.AssignLiteral("explicit/images/content/used/raw");
-    } else if (mType == ContentUsedUncompressed) {
-      path.AssignLiteral("explicit/images/content/used/uncompressed");
+    } else if (mType == ContentUsedUncompressedHeap) {
+      path.AssignLiteral("explicit/images/content/used/uncompressed-heap");
+    } else if (mType == ContentUsedUncompressedNonheap) {
+      path.AssignLiteral("explicit/images/content/used/uncompressed-nonheap");
     } else if (mType == ContentUnusedRaw) {
       path.AssignLiteral("explicit/images/content/unused/raw");
-    } else if (mType == ContentUnusedUncompressed) {
-      path.AssignLiteral("explicit/images/content/unused/uncompressed");
+    } else if (mType == ContentUnusedUncompressedHeap) {
+      path.AssignLiteral("explicit/images/content/unused/uncompressed-heap");
+    } else if (mType == ContentUnusedUncompressedNonheap) {
+      path.AssignLiteral("explicit/images/content/unused/uncompressed-nonheap");
     }
     return NS_OK;
   }
 
   NS_IMETHOD GetKind(PRInt32 *kind)
   {
-    *kind = KIND_HEAP;
+    if (mType & HEAP_BIT) {
+      *kind = KIND_HEAP;
+    }
+    else {
+      *kind = KIND_NONHEAP;
+    }
     return NS_OK;
   }
 
   NS_IMETHOD GetUnits(PRInt32 *units)
   {
     *units = UNITS_BYTES;
     return NS_OK;
   }
@@ -225,19 +248,21 @@ public:
     }
 
     nsRefPtr<imgRequest> req = entry->GetRequest();
     Image *image = static_cast<Image*>(req->mImage.get());
     if (!image)
       return PL_DHASH_NEXT;
 
     if (rtype & RAW_BIT) {
-      arg->value += image->GetSourceDataSize();
+      arg->value += image->GetSourceHeapSize();
+    } else if (rtype & HEAP_BIT) {
+      arg->value += image->GetDecodedHeapSize();
     } else {
-      arg->value += image->GetDecodedDataSize();
+      arg->value += image->GetDecodedNonheapSize();
     }
 
     return PL_DHASH_NEXT;
   }
 
   NS_IMETHOD GetAmount(PRInt64 *amount)
   {
     EnumArg arg(mType);
@@ -250,29 +275,37 @@ public:
     *amount = arg.value;
     return NS_OK;
   }
 
   NS_IMETHOD GetDescription(nsACString &desc)
   {
     if (mType == ChromeUsedRaw) {
       desc.AssignLiteral("Memory used by in-use chrome images (compressed data).");
-    } else if (mType == ChromeUsedUncompressed) {
+    } else if (mType == ChromeUsedUncompressedHeap) {
+      desc.AssignLiteral("Memory used by in-use chrome images (uncompressed data).");
+    } else if (mType == ChromeUsedUncompressedNonheap) {
       desc.AssignLiteral("Memory used by in-use chrome images (uncompressed data).");
     } else if (mType == ChromeUnusedRaw) {
       desc.AssignLiteral("Memory used by not in-use chrome images (compressed data).");
-    } else if (mType == ChromeUnusedUncompressed) {
+    } else if (mType == ChromeUnusedUncompressedHeap) {
+      desc.AssignLiteral("Memory used by not in-use chrome images (uncompressed data).");
+    } else if (mType == ChromeUnusedUncompressedNonheap) {
       desc.AssignLiteral("Memory used by not in-use chrome images (uncompressed data).");
     } else if (mType == ContentUsedRaw) {
       desc.AssignLiteral("Memory used by in-use content images (compressed data).");
-    } else if (mType == ContentUsedUncompressed) {
+    } else if (mType == ContentUsedUncompressedHeap) {
+      desc.AssignLiteral("Memory used by in-use content images (uncompressed data).");
+    } else if (mType == ContentUsedUncompressedNonheap) {
       desc.AssignLiteral("Memory used by in-use content images (uncompressed data).");
     } else if (mType == ContentUnusedRaw) {
       desc.AssignLiteral("Memory used by not in-use content images (compressed data).");
-    } else if (mType == ContentUnusedUncompressed) {
+    } else if (mType == ContentUnusedUncompressedHeap) {
+      desc.AssignLiteral("Memory used by not in-use content images (uncompressed data).");
+    } else if (mType == ContentUnusedUncompressedNonheap) {
       desc.AssignLiteral("Memory used by not in-use content images (uncompressed data).");
     }
     return NS_OK;
   }
 
   ReporterType mType;
 };
 
@@ -876,23 +909,27 @@ nsresult imgLoader::InitCache()
   PRInt32 cachesize;
   rv = Preferences::GetInt("image.cache.size", &cachesize);
   if (NS_SUCCEEDED(rv))
     sCacheMaxSize = cachesize;
   else
     sCacheMaxSize = 5 * 1024 * 1024;
 
   NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUsedRaw));
-  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUsedUncompressed));
+  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUsedUncompressedHeap));
+  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUsedUncompressedNonheap));
   NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUnusedRaw));
-  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUnusedUncompressed));
+  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUnusedUncompressedHeap));
+  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ChromeUnusedUncompressedNonheap));
   NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUsedRaw));
-  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUsedUncompressed));
+  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUsedUncompressedHeap));
+  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUsedUncompressedNonheap));
   NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUnusedRaw));
-  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUnusedUncompressed));
+  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUnusedUncompressedHeap));
+  NS_RegisterMemoryReporter(new imgMemoryReporter(imgMemoryReporter::ContentUnusedUncompressedNonheap));
   
   return NS_OK;
 }
 
 nsresult imgLoader::Init()
 {
   ReadAcceptHeaderPref();