Bug 910517 (3rd attempt) - Remove nsIMemoryReporter, and rename nsIMemoryMultiReporter as nsIMemoryReporter. r=mmcr8.
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 27 Aug 2013 16:24:51 -0700
changeset 149373 3fe9649ebd84b0a4bdefc1321172ada924cb4372
parent 149372 6b80e6b1fea2fdd81a73dd74984f8561d51fb861
child 149375 fd077e283f6a22e024ab2076963b1e6967ec3996
push idunknown
push userunknown
push dateunknown
reviewersmmcr8
bugs910517
milestone26.0a1
Bug 910517 (3rd attempt) - Remove nsIMemoryReporter, and rename nsIMemoryMultiReporter as nsIMemoryReporter. r=mmcr8.
addon-sdk/source/lib/sdk/test/harness.js
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMFile.cpp
content/canvas/src/CanvasRenderingContext2D.cpp
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextReporter.cpp
content/canvas/src/WebGLMemoryMultiReporterWrapper.h
content/canvas/src/WebGLMemoryReporterWrapper.h
content/media/MediaDecoder.cpp
dom/base/nsScriptNameSpaceManager.cpp
dom/base/nsWindowMemoryReporter.cpp
dom/base/nsWindowMemoryReporter.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
extensions/spellcheck/hunspell/src/mozHunspell.cpp
gfx/gl/GfxTexturesReporter.h
gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp
gfx/thebes/gfxASurface.cpp
gfx/thebes/gfxAndroidPlatform.cpp
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxPlatformFontList.h
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
image/src/imgLoader.cpp
image/test/mochitest/test_bug601470.html
ipc/glue/SharedMemory.cpp
js/xpconnect/src/XPCJSMemoryReporter.h
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcpublic.h
layout/base/nsStyleSheetService.cpp
layout/style/nsLayoutStylesheetCache.cpp
modules/libpref/src/Preferences.cpp
netwerk/cache/nsDiskCacheDevice.cpp
netwerk/cache/nsMemoryCacheDevice.cpp
netwerk/dns/nsEffectiveTLDService.cpp
startupcache/StartupCache.cpp
storage/src/mozStorageService.cpp
storage/src/mozStorageService.h
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/aboutmemory/tests/test_aboutcompartments.xul
toolkit/components/aboutmemory/tests/test_aboutmemory.xul
toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
toolkit/components/aboutmemory/tests/test_memoryReporters.xul
toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul
toolkit/components/places/History.cpp
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/TelemetryPing.js
toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
xpcom/base/AvailableMemoryTracker.cpp
xpcom/base/MapsMemoryReporter.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsIMemoryReporter.idl
xpcom/base/nsMemoryInfoDumper.cpp
xpcom/base/nsMemoryReporterManager.cpp
xpcom/base/nsMemoryReporterManager.h
xpcom/build/nsXPComInit.cpp
xpcom/components/nsCategoryManager.cpp
xpcom/components/nsComponentManager.cpp
xpcom/ds/nsObserverService.cpp
xpcom/ds/nsObserverService.h
xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -143,16 +143,19 @@ function dictDiff(last, curr) {
 }
 
 function reportMemoryUsage() {
   memory.gc();
 
   var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
             .getService(Ci.nsIMemoryReporterManager);
 
+  // XXX: this code is *so* bogus -- nsIMemoryReporter changed its |memoryUsed|
+  // field to |amount| *years* ago, and even bigger changes have happened
+  // since -- that it must just never be run.
   var reporters = mgr.enumerateReporters();
   if (reporters.hasMoreElements())
     print("\n");
 
   while (reporters.hasMoreElements()) {
     var reporter = reporters.getNext();
     reporter.QueryInterface(Ci.nsIMemoryReporter);
     print(reporter.description + ": " + reporter.memoryUsed + "\n");
@@ -371,24 +374,17 @@ function getPotentialLeaks() {
     }
   }
 
   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
 
   let enm = mgr.enumerateReporters();
   while (enm.hasMoreElements()) {
-    let reporter = enm.getNext().QueryInterface(Ci.nsIMemoryReporter);
-    logReporter(reporter.process, reporter.path, reporter.kind, reporter.units,
-                reporter.amount, reporter.description);
-  }
-
-  let enm = mgr.enumerateMultiReporters();
-  while (enm.hasMoreElements()) {
-    let mr = enm.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
+    let mr = enm.getNext().QueryInterface(Ci.nsIMemoryReporter);
     mr.collectReports(logReporter, null);
   }
 
   return { compartments: compartments, windows: windows };
 }
 
 function nextIteration(tests) {
   if (tests) {
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -252,21 +252,21 @@ bool nsContentUtils::sDOMWindowDumpEnabl
 namespace {
 
 static const char kJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1";
 static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
 static PLDHashTable sEventListenerManagersHash;
 
-class DOMEventListenerManagersHashReporter MOZ_FINAL : public MemoryReporterBase
+class DOMEventListenerManagersHashReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   DOMEventListenerManagersHashReporter()
-    : MemoryReporterBase(
+    : MemoryUniReporter(
         "explicit/dom/event-listener-managers-hash",
         KIND_HEAP,
         UNITS_BYTES,
         "Memory used by the event listener manager's hash table.")
   {}
 
 private:
   int64_t Amount()
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -636,27 +636,27 @@ nsDOMMemoryFile::DataOwner::sDataOwnerMu
 nsDOMMemoryFile::DataOwner::sDataOwners;
 
 /* static */ bool
 nsDOMMemoryFile::DataOwner::sMemoryReporterRegistered;
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(DOMMemoryFileDataOwnerMallocSizeOf)
 
 class nsDOMMemoryFileDataOwnerMemoryReporter MOZ_FINAL
-  : public nsIMemoryMultiReporter
+  : public nsIMemoryReporter
 {
   NS_DECL_THREADSAFE_ISUPPORTS
 
   NS_IMETHOD GetName(nsACString& aName)
   {
     aName.AssignASCII("dom-memory-file-data-owner");
     return NS_OK;
   }
 
-  NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCallback,
+  NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCallback,
                             nsISupports *aClosure)
   {
     typedef nsDOMMemoryFile::DataOwner DataOwner;
 
     StaticMutexAutoLock lock(DataOwner::sDataOwnerMutex);
 
     if (!DataOwner::sDataOwners) {
       return NS_OK;
@@ -720,29 +720,29 @@ class nsDOMMemoryFileDataOwnerMemoryRepo
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS1(nsDOMMemoryFileDataOwnerMemoryReporter,
-                   nsIMemoryMultiReporter)
+                   nsIMemoryReporter)
 
 /* static */ void
 nsDOMMemoryFile::DataOwner::EnsureMemoryReporterRegistered()
 {
   sDataOwnerMutex.AssertCurrentThreadOwns();
   if (sMemoryReporterRegistered) {
     return;
   }
 
   nsRefPtr<nsDOMMemoryFileDataOwnerMemoryReporter> reporter = new
     nsDOMMemoryFileDataOwnerMemoryReporter();
-  NS_RegisterMemoryMultiReporter(reporter);
+  NS_RegisterMemoryReporter(reporter);
 
   sMemoryReporterRegistered = true;
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // nsDOMFileList implementation
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsDOMFileList)
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -128,21 +128,21 @@ namespace dom {
 const Float SIGMA_MAX = 100;
 
 /* Memory reporter stuff */
 static int64_t gCanvasAzureMemoryUsed = 0;
 
 // This is KIND_OTHER because it's not always clear where in memory the pixels
 // of a canvas are stored.  Furthermore, this memory will be tracked by the
 // underlying surface implementations.  See bug 655638 for details.
-class Canvas2dPixelsReporter MOZ_FINAL : public MemoryReporterBase
+class Canvas2dPixelsReporter MOZ_FINAL : public MemoryUniReporter
 {
   public:
     Canvas2dPixelsReporter()
-      : MemoryReporterBase("canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
 "Memory used by 2D canvases. Each canvas requires (width * height * 4) bytes.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE { return gCanvasAzureMemoryUsed; }
 };
 
 class CanvasRadialGradient : public CanvasGradient
 {
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -5,17 +5,17 @@
 
 #include "WebGLContext.h"
 #include "WebGL1Context.h"
 #include "WebGLObjectModel.h"
 #include "WebGLExtensions.h"
 #include "WebGLContextUtils.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
-#include "WebGLMemoryMultiReporterWrapper.h"
+#include "WebGLMemoryReporterWrapper.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLVertexArray.h"
 #include "WebGLQuery.h"
 
 #include "AccessCheck.h"
 #include "nsIConsoleService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIClassInfoImpl.h"
@@ -174,17 +174,17 @@ WebGLContext::WebGLContext()
     mGLMaxColorAttachments = 1;
     mGLMaxDrawBuffers = 1;
     mGLMaxTransformFeedbackSeparateAttribs = 0;
 
     // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
     mPixelStorePackAlignment = 4;
     mPixelStoreUnpackAlignment = 4;
 
-    WebGLMemoryMultiReporterWrapper::AddWebGLContext(this);
+    WebGLMemoryReporterWrapper::AddWebGLContext(this);
 
     mAllowRestore = true;
     mContextLossTimerRunning = false;
     mDrawSinceContextLossTimerSet = false;
     mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
     mContextStatus = ContextNotLost;
     mContextLostErrorSet = false;
     mLoseContextOnHeapMinimize = false;
@@ -208,17 +208,17 @@ WebGLContext::WebGLContext()
     mDisableFragHighP = false;
 
     mDrawCallsSinceLastFlush = 0;
 }
 
 WebGLContext::~WebGLContext()
 {
     DestroyResourcesAndContext();
-    WebGLMemoryMultiReporterWrapper::RemoveWebGLContext(this);
+    WebGLMemoryReporterWrapper::RemoveWebGLContext(this);
     TerminateContextLossTimer();
     mContextRestorer = nullptr;
 }
 
 void
 WebGLContext::DestroyResourcesAndContext()
 {
     if (mMemoryPressureObserver) {
@@ -648,18 +648,18 @@ void WebGLContext::LoseOldestWebGLContex
 #endif
     MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
 
     // it's important to update the index on a new context before losing old contexts,
     // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
     // when choosing which one to lose first.
     UpdateLastUseIndex();
 
-    WebGLMemoryMultiReporterWrapper::ContextsArrayType &contexts
-      = WebGLMemoryMultiReporterWrapper::Contexts();
+    WebGLMemoryReporterWrapper::ContextsArrayType &contexts
+      = WebGLMemoryReporterWrapper::Contexts();
 
     // quick exit path, should cover a majority of cases
     if (contexts.Length() <= kMaxWebGLContextsPerPrincipal) {
         return;
     }
 
     // note that here by "context" we mean "non-lost context". See the check for
     // IsContextLost() below. Indeed, the point of this function is to maybe lose
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -113,17 +113,17 @@ class WebGLContext :
     public nsIDOMWebGLRenderingContext,
     public nsICanvasRenderingContextInternal,
     public nsSupportsWeakReference,
     public WebGLRectangleObject,
     public nsWrapperCache
 {
     friend class WebGLContextUserData;
     friend class WebGLMemoryPressureObserver;
-    friend class WebGLMemoryMultiReporterWrapper;
+    friend class WebGLMemoryReporterWrapper;
     friend class WebGLExtensionLoseContext;
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionCompressedTextureATC;
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionDepthTexture;
     friend class WebGLExtensionDrawBuffers;
     friend class WebGLExtensionVertexArray;
 
--- a/content/canvas/src/WebGLContextReporter.cpp
+++ b/content/canvas/src/WebGLContextReporter.cpp
@@ -1,165 +1,165 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLContext.h"
-#include "WebGLMemoryMultiReporterWrapper.h"
+#include "WebGLMemoryReporterWrapper.h"
 #include "nsIMemoryReporter.h"
 
 using namespace mozilla;
 
 NS_IMPL_ISUPPORTS1(WebGLMemoryPressureObserver, nsIObserver)
 
-class WebGLMemoryMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter 
+class WebGLMemoryReporter MOZ_FINAL : public nsIMemoryReporter
 {
   public:
     NS_DECL_ISUPPORTS
-    NS_DECL_NSIMEMORYMULTIREPORTER
+    NS_DECL_NSIMEMORYREPORTER
 };
 
-NS_IMPL_ISUPPORTS1(WebGLMemoryMultiReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(WebGLMemoryReporter, nsIMemoryReporter)
 
 NS_IMETHODIMP
-WebGLMemoryMultiReporter::GetName(nsACString &aName)
+WebGLMemoryReporter::GetName(nsACString &aName)
 {
   aName.AssignLiteral("webgl");
   return NS_OK;
 }
 
 NS_IMETHODIMP
-WebGLMemoryMultiReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, 
-                                         nsISupports* aClosure)
+WebGLMemoryReporter::CollectReports(nsIMemoryReporterCallback* aCb,
+                                    nsISupports* aClosure)
 {
 #define REPORT(_path, _kind, _units, _amount, _desc)                          \
     do {                                                                      \
       nsresult rv;                                                            \
       rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), _kind,    \
                          _units, _amount, NS_LITERAL_CSTRING(_desc),          \
                          aClosure);                                           \
       NS_ENSURE_SUCCESS(rv, rv);                                              \
     } while (0)
 
     REPORT("webgl-texture-memory",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetTextureMemoryUsed(),
+           WebGLMemoryReporterWrapper::GetTextureMemoryUsed(),
            "Memory used by WebGL textures.The OpenGL"
            " implementation is free to store these textures in either video"
            " memory or main memory. This measurement is only a lower bound,"
-           " actual memory usage may be higher for example if the storage" 
+           " actual memory usage may be higher for example if the storage"
            " is strided.");
-   
+
     REPORT("webgl-texture-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetTextureCount(), 
+           WebGLMemoryReporterWrapper::GetTextureCount(),
            "Number of WebGL textures.");
 
     REPORT("webgl-buffer-memory",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetBufferMemoryUsed(), 
+           WebGLMemoryReporterWrapper::GetBufferMemoryUsed(),
            "Memory used by WebGL buffers. The OpenGL"
            " implementation is free to store these buffers in either video"
            " memory or main memory. This measurement is only a lower bound,"
            " actual memory usage may be higher for example if the storage"
            " is strided.");
 
     REPORT("explicit/webgl/buffer-cache-memory",
            nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetBufferCacheMemoryUsed(),
+           WebGLMemoryReporterWrapper::GetBufferCacheMemoryUsed(),
            "Memory used by WebGL buffer caches. The WebGL"
            " implementation caches the contents of element array buffers"
            " only.This adds up with the webgl-buffer-memory value, but"
            " contrary to it, this one represents bytes on the heap,"
            " not managed by OpenGL.");
 
     REPORT("webgl-buffer-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetBufferCount(),
-           "Number of WebGL buffers."); 
-   
+           WebGLMemoryReporterWrapper::GetBufferCount(),
+           "Number of WebGL buffers.");
+
     REPORT("webgl-renderbuffer-memory",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetRenderbufferMemoryUsed(), 
+           WebGLMemoryReporterWrapper::GetRenderbufferMemoryUsed(),
            "Memory used by WebGL renderbuffers. The OpenGL"
            " implementation is free to store these renderbuffers in either"
            " video memory or main memory. This measurement is only a lower"
            " bound, actual memory usage may be higher for example if the"
            " storage is strided.");
-   
+
     REPORT("webgl-renderbuffer-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetRenderbufferCount(),
+           WebGLMemoryReporterWrapper::GetRenderbufferCount(),
            "Number of WebGL renderbuffers.");
-  
+
     REPORT("explicit/webgl/shader",
            nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
-           WebGLMemoryMultiReporterWrapper::GetShaderSize(), 
+           WebGLMemoryReporterWrapper::GetShaderSize(),
            "Combined size of WebGL shader ASCII sources and translation"
-           " logs cached on the heap."); 
+           " logs cached on the heap.");
 
     REPORT("webgl-shader-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetShaderCount(), 
-           "Number of WebGL shaders."); 
+           WebGLMemoryReporterWrapper::GetShaderCount(),
+           "Number of WebGL shaders.");
 
     REPORT("webgl-context-count",
            nsIMemoryReporter::KIND_OTHER, nsIMemoryReporter::UNITS_COUNT,
-           WebGLMemoryMultiReporterWrapper::GetContextCount(), 
-           "Number of WebGL contexts."); 
+           WebGLMemoryReporterWrapper::GetContextCount(),
+           "Number of WebGL contexts.");
 
 #undef REPORT
 
     return NS_OK;
 }
 
-WebGLMemoryMultiReporterWrapper* WebGLMemoryMultiReporterWrapper::sUniqueInstance = nullptr;
+WebGLMemoryReporterWrapper* WebGLMemoryReporterWrapper::sUniqueInstance = nullptr;
 
-WebGLMemoryMultiReporterWrapper* WebGLMemoryMultiReporterWrapper::UniqueInstance()
+WebGLMemoryReporterWrapper* WebGLMemoryReporterWrapper::UniqueInstance()
 {
     if (!sUniqueInstance) {
-        sUniqueInstance = new WebGLMemoryMultiReporterWrapper;
+        sUniqueInstance = new WebGLMemoryReporterWrapper;
     }
-    return sUniqueInstance;     
+    return sUniqueInstance;
 }
 
-WebGLMemoryMultiReporterWrapper::WebGLMemoryMultiReporterWrapper()
-{ 
-    mReporter = new WebGLMemoryMultiReporter;   
-    NS_RegisterMemoryMultiReporter(mReporter);
+WebGLMemoryReporterWrapper::WebGLMemoryReporterWrapper()
+{
+    mReporter = new WebGLMemoryReporter;
+    NS_RegisterMemoryReporter(mReporter);
 }
 
-WebGLMemoryMultiReporterWrapper::~WebGLMemoryMultiReporterWrapper()
+WebGLMemoryReporterWrapper::~WebGLMemoryReporterWrapper()
 {
-    NS_UnregisterMemoryMultiReporter(mReporter);
+    NS_UnregisterMemoryReporter(mReporter);
 }
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(WebGLBufferMallocSizeOf)
 
-int64_t 
-WebGLMemoryMultiReporterWrapper::GetBufferCacheMemoryUsed() {
+int64_t
+WebGLMemoryReporterWrapper::GetBufferCacheMemoryUsed() {
     const ContextsArrayType & contexts = Contexts();
     int64_t result = 0;
     for(size_t i = 0; i < contexts.Length(); ++i) {
         for (const WebGLBuffer *buffer = contexts[i]->mBuffers.getFirst();
              buffer;
              buffer = buffer->getNext())
         {
             if (buffer->Target() == LOCAL_GL_ELEMENT_ARRAY_BUFFER)
                 result += buffer->SizeOfIncludingThis(WebGLBufferMallocSizeOf);
         }
     }
     return result;
 }
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(WebGLShaderMallocSizeOf)
 
-int64_t 
-WebGLMemoryMultiReporterWrapper::GetShaderSize() {
+int64_t
+WebGLMemoryReporterWrapper::GetShaderSize() {
     const ContextsArrayType & contexts = Contexts();
     int64_t result = 0;
     for(size_t i = 0; i < contexts.Length(); ++i) {
         for (const WebGLShader *shader = contexts[i]->mShaders.getFirst();
              shader;
              shader = shader->getNext())
         {
             result += shader->SizeOfIncludingThis(WebGLShaderMallocSizeOf);
rename from content/canvas/src/WebGLMemoryMultiReporterWrapper.h
rename to content/canvas/src/WebGLMemoryReporterWrapper.h
--- a/content/canvas/src/WebGLMemoryMultiReporterWrapper.h
+++ b/content/canvas/src/WebGLMemoryReporterWrapper.h
@@ -1,42 +1,42 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef WEBGLMEMORYMULTIREPORTWRAPER_H_
-#define WEBGLMEMORYMULTIREPORTWRAPER_H_
+#ifndef WEBGLMEMORYREPORTERWRAPPER_H_
+#define WEBGLMEMORYREPORTERWRAPPER_H_
 
 #include "WebGLContext.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
 #include "WebGLShader.h"
 #include "WebGLProgram.h"
 #include "WebGLUniformLocation.h"
 #include "WebGLTexture.h"
 #include "WebGLRenderbuffer.h"
 
 namespace mozilla {
 
-class WebGLMemoryMultiReporterWrapper
+class WebGLMemoryReporterWrapper
 {
-    WebGLMemoryMultiReporterWrapper();
-    ~WebGLMemoryMultiReporterWrapper();
-    static WebGLMemoryMultiReporterWrapper* sUniqueInstance;
+    WebGLMemoryReporterWrapper();
+    ~WebGLMemoryReporterWrapper();
+    static WebGLMemoryReporterWrapper* sUniqueInstance;
 
-    // here we store plain pointers, not RefPtrs: we don't want the 
-    // WebGLMemoryMultiReporterWrapper unique instance to keep alive all		
+    // here we store plain pointers, not RefPtrs: we don't want the
+    // WebGLMemoryReporterWrapper unique instance to keep alive all
     // WebGLContexts ever created.
     typedef nsTArray<const WebGLContext*> ContextsArrayType;
     ContextsArrayType mContexts;
 
-    nsCOMPtr<nsIMemoryMultiReporter> mReporter;
+    nsCOMPtr<nsIMemoryReporter> mReporter;
 
-    static WebGLMemoryMultiReporterWrapper* UniqueInstance();
+    static WebGLMemoryReporterWrapper* UniqueInstance();
 
     static ContextsArrayType & Contexts() { return UniqueInstance()->mContexts; }
 
     friend class WebGLContext;
 
   public:
 
     static void AddWebGLContext(const WebGLContext* c) {
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -68,17 +68,17 @@ class MediaMemoryTracker
 
   typedef nsTArray<MediaDecoder*> DecodersArray;
   static DecodersArray& Decoders() {
     return UniqueInstance()->mDecoders;
   }
 
   DecodersArray mDecoders;
 
-  nsCOMPtr<nsIMemoryMultiReporter> mReporter;
+  nsCOMPtr<nsIMemoryReporter> mReporter;
 
 public:
   static void AddMediaDecoder(MediaDecoder* aDecoder)
   {
     Decoders().AppendElement(aDecoder);
   }
 
   static void RemoveMediaDecoder(MediaDecoder* aDecoder)
@@ -1723,28 +1723,28 @@ MediaDecoder::IsDASHEnabled()
 #ifdef MOZ_WMF
 bool
 MediaDecoder::IsWMFEnabled()
 {
   return WMFDecoder::IsEnabled();
 }
 #endif
 
-class MediaReporter MOZ_FINAL : public nsIMemoryMultiReporter
+class MediaReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD GetName(nsACString& aName)
   {
     aName.AssignLiteral("media");
     return NS_OK;
   }
 
-  NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback* aCb,
+  NS_IMETHOD CollectReports(nsIMemoryReporterCallback* aCb,
                             nsISupports* aClosure)
   {
     int64_t video, audio;
     MediaMemoryTracker::GetAmounts(&video, &audio);
 
   #define REPORT(_path, _amount, _desc)                                       \
     do {                                                                      \
         nsresult rv;                                                          \
@@ -1760,30 +1760,30 @@ public:
 
     REPORT("explicit/media/decoded-audio", audio,
            "Memory used by decoded audio chunks.");
 
     return NS_OK;
   }
 };
 
-NS_IMPL_ISUPPORTS1(MediaReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(MediaReporter, nsIMemoryReporter)
 
 MediaDecoderOwner*
 MediaDecoder::GetOwner()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mOwner;
 }
 
 MediaMemoryTracker::MediaMemoryTracker()
   : mReporter(new MediaReporter())
 {
-  NS_RegisterMemoryMultiReporter(mReporter);
+  NS_RegisterMemoryReporter(mReporter);
 }
 
 MediaMemoryTracker::~MediaMemoryTracker()
 {
-  NS_UnregisterMemoryMultiReporter(mReporter);
+  NS_UnregisterMemoryReporter(mReporter);
 }
 
 } // namespace mozilla
 
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -111,21 +111,21 @@ GlobalNameHashInitEntry(PLDHashTable *ta
   new (&e->mKey) nsString(*keyStr);
 
   // This will set e->mGlobalName.mType to
   // nsGlobalNameStruct::eTypeNotInitialized
   memset(&e->mGlobalName, 0, sizeof(nsGlobalNameStruct));
   return true;
 }
 
-class ScriptNameSpaceManagerReporter MOZ_FINAL : public MemoryReporterBase
+class ScriptNameSpaceManagerReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   ScriptNameSpaceManagerReporter(nsScriptNameSpaceManager* aManager)
-    : MemoryReporterBase(
+    : MemoryUniReporter(
         "explicit/script-namespace-manager",
         KIND_HEAP,
         nsIMemoryReporter::UNITS_BYTES,
         "Memory used for the script namespace manager.")
     , mManager(aManager)
   {}
 
 private:
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -19,44 +19,44 @@
 
 using namespace mozilla;
 
 nsWindowMemoryReporter::nsWindowMemoryReporter()
   : mCheckForGhostWindowsCallbackPending(false)
 {
 }
 
-NS_IMPL_ISUPPORTS3(nsWindowMemoryReporter, nsIMemoryMultiReporter, nsIObserver,
+NS_IMPL_ISUPPORTS3(nsWindowMemoryReporter, nsIMemoryReporter, nsIObserver,
                    nsSupportsWeakReference)
 
 /* static */
 void
 nsWindowMemoryReporter::Init()
 {
   // The memory reporter manager will own this object.
   nsRefPtr<nsWindowMemoryReporter> windowReporter = new nsWindowMemoryReporter();
-  NS_RegisterMemoryMultiReporter(windowReporter);
+  NS_RegisterMemoryReporter(windowReporter);
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     // DOM_WINDOW_DESTROYED_TOPIC announces what we call window "detachment",
     // when a window's docshell is set to NULL.
     os->AddObserver(windowReporter, DOM_WINDOW_DESTROYED_TOPIC,
                     /* weakRef = */ true);
     os->AddObserver(windowReporter, "after-minimize-memory-usage",
                     /* weakRef = */ true);
   }
 
-  nsRefPtr<GhostURLsReporter> ghostMultiReporter =
+  nsRefPtr<GhostURLsReporter> ghostURLsReporter =
     new GhostURLsReporter(windowReporter);
-  NS_RegisterMemoryMultiReporter(ghostMultiReporter);
+  NS_RegisterMemoryReporter(ghostURLsReporter);
 
-  nsRefPtr<NumGhostsReporter> ghostReporter =
+  nsRefPtr<NumGhostsReporter> numGhostsReporter =
     new NumGhostsReporter(windowReporter);
-  NS_RegisterMemoryReporter(ghostReporter);
+  NS_RegisterMemoryReporter(numGhostsReporter);
 }
 
 static already_AddRefed<nsIURI>
 GetWindowURI(nsIDOMWindow *aWindow)
 {
   nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(aWindow);
   NS_ENSURE_TRUE(pWindow, nullptr);
 
@@ -116,17 +116,17 @@ typedef nsDataHashtable<nsUint64HashKey,
 
 static nsresult
 CollectWindowReports(nsGlobalWindow *aWindow,
                      amIAddonManager *addonManager,
                      nsWindowSizes *aWindowTotalSizes,
                      nsTHashtable<nsUint64HashKey> *aGhostWindowIDs,
                      WindowPaths *aWindowPaths,
                      WindowPaths *aTopWindowPaths,
-                     nsIMemoryMultiReporterCallback *aCb,
+                     nsIMemoryReporterCallback *aCb,
                      nsISupports *aClosure)
 {
   nsAutoCString windowPath("explicit/");
 
   // Avoid calling aWindow->GetTop() if there's no outer window.  It will work
   // just fine, but will spew a lot of warnings.
   nsGlobalWindow *top = NULL;
   nsCOMPtr<nsIURI> location;
@@ -314,17 +314,17 @@ GetWindows(const uint64_t& aId, nsGlobal
 NS_IMETHODIMP
 nsWindowMemoryReporter::GetName(nsACString &aName)
 {
   aName.AssignLiteral("window-objects");
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWindowMemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb,
+nsWindowMemoryReporter::CollectReports(nsIMemoryReporterCallback* aCb,
                                        nsISupports* aClosure)
 {
   nsGlobalWindow::WindowByIdTable* windowsById =
     nsGlobalWindow::GetWindowsTable();
   NS_ENSURE_TRUE(windowsById, NS_OK);
 
   // Hold on to every window in memory so that window objects can't be
   // destroyed while we're calling the memory reporter callback.
@@ -347,19 +347,19 @@ nsWindowMemoryReporter::CollectReports(n
     nsresult rv = CollectWindowReports(windows[i], addonManager,
                                        &windowTotalSizes, &ghostWindows,
                                        &windowPaths, &topWindowPaths, aCb,
                                        aClosure);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Report JS memory usage.  We do this from here because the JS memory
-  // multi-reporter needs to be passed |windowPaths|.
-  nsresult rv = xpc::JSMemoryMultiReporter::CollectReports(&windowPaths, &topWindowPaths,
-                                                           aCb, aClosure);
+  // reporter needs to be passed |windowPaths|.
+  nsresult rv = xpc::JSReporter::CollectReports(&windowPaths, &topWindowPaths,
+                                                aCb, aClosure);
   NS_ENSURE_SUCCESS(rv, rv);
 
 #define REPORT(_path, _amount, _desc)                                         \
   do {                                                                        \
     nsresult rv;                                                              \
     rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path),             \
                        nsIMemoryReporter::KIND_OTHER,                         \
                        nsIMemoryReporter::UNITS_BYTES, _amount,               \
@@ -658,36 +658,36 @@ nsWindowMemoryReporter::CheckForGhostWin
   CheckForGhostWindowsEnumeratorData ghostEnumData =
     { &nonDetachedWindowDomains, aOutGhostIDs, tldService,
       GetGhostTimeout(), TimeStamp::Now() };
   mDetachedWindows.Enumerate(CheckForGhostWindowsEnumerator,
                              &ghostEnumData);
 }
 
 NS_IMPL_ISUPPORTS1(nsWindowMemoryReporter::GhostURLsReporter,
-                   nsIMemoryMultiReporter)
+                   nsIMemoryReporter)
 
 nsWindowMemoryReporter::
 GhostURLsReporter::GhostURLsReporter(
   nsWindowMemoryReporter* aWindowReporter)
   : mWindowReporter(aWindowReporter)
 {
 }
 
 NS_IMETHODIMP
 nsWindowMemoryReporter::
 GhostURLsReporter::GetName(nsACString& aName)
 {
-  aName.AssignLiteral("ghost-windows");
+  aName.AssignLiteral("ghost-windows-multi");
   return NS_OK;
 }
 
 struct ReportGhostWindowsEnumeratorData
 {
-  nsIMemoryMultiReporterCallback* callback;
+  nsIMemoryReporterCallback* callback;
   nsISupports* closure;
   nsresult rv;
 };
 
 static PLDHashOperator
 ReportGhostWindowsEnumerator(nsUint64HashKey* aIDHashKey, void* aClosure)
 {
   ReportGhostWindowsEnumeratorData *data =
@@ -724,51 +724,32 @@ ReportGhostWindowsEnumerator(nsUint64Has
   }
 
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 nsWindowMemoryReporter::
 GhostURLsReporter::CollectReports(
-  nsIMemoryMultiReporterCallback* aCb,
+  nsIMemoryReporterCallback* aCb,
   nsISupports* aClosure)
 {
   // Get the IDs of all the ghost windows in existance.
   nsTHashtable<nsUint64HashKey> ghostWindows;
   mWindowReporter->CheckForGhostWindows(&ghostWindows);
 
   ReportGhostWindowsEnumeratorData reportGhostWindowsEnumData =
     { aCb, aClosure, NS_OK };
 
   // Call aCb->Callback() for each ghost window.
   ghostWindows.EnumerateEntries(ReportGhostWindowsEnumerator,
                                 &reportGhostWindowsEnumData);
 
   return reportGhostWindowsEnumData.rv;
 }
 
-NS_IMETHODIMP
-nsWindowMemoryReporter::
-NumGhostsReporter::GetDescription(nsACString& aDesc)
-{
-  nsPrintfCString str(
-"The number of ghost windows present (the number of nodes underneath \
-explicit/window-objects/top(none)/ghost, modulo race conditions).  A ghost \
-window is not shown in any tab, does not share a domain with any non-detached \
-windows, and has met these criteria for at least %ds \
-(memory.ghost_window_timeout_seconds) or has survived a round of about:memory's \
-minimize memory usage button.\n\n\
-Ghost windows can happen legitimately, but they are often indicative of leaks \
-in the browser or add-ons.",
-  mWindowReporter->GetGhostTimeout());
-
-  aDesc.Assign(str);
-  return NS_OK;
-}
-
 int64_t
 nsWindowMemoryReporter::NumGhostsReporter::Amount()
 {
   nsTHashtable<nsUint64HashKey> ghostWindows;
   mWindowReporter->CheckForGhostWindows(&ghostWindows);
   return ghostWindows.Count();
 }
--- a/dom/base/nsWindowMemoryReporter.h
+++ b/dom/base/nsWindowMemoryReporter.h
@@ -103,61 +103,65 @@ public:
  *   the top level chrome window).  Exposing this ensures that each tab gets
  *   its own sub-tree, even if multiple tabs are showing the same URI.
  *
  * - <top-uri> is the URI of the top window.  Excepting special windows (such
  *   as browser.xul or hiddenWindow.html) it's what the address bar shows for
  *   the tab.
  *
  */
-class nsWindowMemoryReporter MOZ_FINAL : public nsIMemoryMultiReporter,
+class nsWindowMemoryReporter MOZ_FINAL : public nsIMemoryReporter,
                                          public nsIObserver,
                                          public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIMEMORYMULTIREPORTER
+  NS_DECL_NSIMEMORYREPORTER
   NS_DECL_NSIOBSERVER
 
   static void Init();
 
 private:
   /**
-   * GhostURLsReporter generates the "ghost-windows" multi-report, which
-   * includes a list of all ghost windows' URLs.  If you're only interested in
-   * this list, running this report is faster than running
-   * nsWindowMemoryReporter.
+   * GhostURLsReporter generates the list of all ghost windows' URLs.  If
+   * you're only interested in this list, running this report is faster than
+   * running nsWindowMemoryReporter.
    */
-  class GhostURLsReporter MOZ_FINAL : public nsIMemoryMultiReporter
+  class GhostURLsReporter MOZ_FINAL : public nsIMemoryReporter
   {
   public:
     GhostURLsReporter(nsWindowMemoryReporter* aWindowReporter);
 
     NS_DECL_ISUPPORTS
-    NS_DECL_NSIMEMORYMULTIREPORTER
+    NS_DECL_NSIMEMORYREPORTER
 
   private:
     nsRefPtr<nsWindowMemoryReporter> mWindowReporter;
   };
 
   /**
-   * nsGhostWindowReporter generates the "ghost-windows" single-report, which
-   * counts the number of ghost windows present.
+   * nsGhostWindowReporter generates the "ghost-windows" report, which counts
+   * the number of ghost windows present.
    */
-  class NumGhostsReporter MOZ_FINAL : public mozilla::MemoryReporterBase
+  class NumGhostsReporter MOZ_FINAL : public mozilla::MemoryUniReporter
   {
   public:
     NumGhostsReporter(nsWindowMemoryReporter* aWindowReporter)
-        // Description is "???" because we define GetDescription below.
-      : MemoryReporterBase("ghost-windows", KIND_OTHER, UNITS_COUNT, "???")
+      : MemoryUniReporter("ghost-windows", KIND_OTHER, UNITS_COUNT,
+"The number of ghost windows present (the number of nodes underneath "
+"explicit/window-objects/top(none)/ghost, modulo race conditions).  A ghost "
+"window is not shown in any tab, does not share a domain with any non-detached "
+"windows, and has met these criteria for at least "
+"memory.ghost_window_timeout_seconds, or has survived a round of "
+"about:memory's minimize memory usage button.\n\n"
+"Ghost windows can happen legitimately, but they are often indicative of "
+"leaks in the browser or add-ons.")
       , mWindowReporter(aWindowReporter)
     {}
 
-    NS_IMETHOD GetDescription(nsACString& aDesc);
-
   private:
     int64_t Amount() MOZ_OVERRIDE;
 
     nsRefPtr<nsWindowMemoryReporter> mWindowReporter;
   };
 
   // Protect ctor, use Init() instead.
   nsWindowMemoryReporter();
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -395,26 +395,26 @@ ContentChild::InitXPCOM()
 
 PMemoryReportRequestChild*
 ContentChild::AllocPMemoryReportRequestChild()
 {
     return new MemoryReportRequestChild();
 }
 
 // This is just a wrapper for InfallibleTArray<MemoryReport> that implements
-// nsISupports, so it can be passed to nsIMemoryMultiReporter::CollectReports.
+// nsISupports, so it can be passed to nsIMemoryReporter::CollectReports.
 class MemoryReportsWrapper MOZ_FINAL : public nsISupports {
 public:
     NS_DECL_ISUPPORTS
     MemoryReportsWrapper(InfallibleTArray<MemoryReport> *r) : mReports(r) { }
     InfallibleTArray<MemoryReport> *mReports;
 };
 NS_IMPL_ISUPPORTS0(MemoryReportsWrapper)
 
-class MemoryReportCallback MOZ_FINAL : public nsIMemoryMultiReporterCallback
+class MemoryReportCallback MOZ_FINAL : public nsIMemoryReporterCallback
 {
 public:
     NS_DECL_ISUPPORTS
 
     MemoryReportCallback(const nsACString &aProcess)
     : mProcess(aProcess)
     {
     }
@@ -432,64 +432,39 @@ public:
         wrappedReports->mReports->AppendElement(memreport);
         return NS_OK;
     }
 private:
     const nsCString mProcess;
 };
 NS_IMPL_ISUPPORTS1(
   MemoryReportCallback
-, nsIMemoryMultiReporterCallback
+, nsIMemoryReporterCallback
 )
 
 bool
 ContentChild::RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* child)
 {
-    
     nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
 
     InfallibleTArray<MemoryReport> reports;
 
     nsPrintfCString process("Content (%d)", getpid());
 
-    // First do the vanilla memory reporters.
+    // Run each reporter.  The callback will turn each measurement into a
+    // MemoryReport.
     nsCOMPtr<nsISimpleEnumerator> e;
     mgr->EnumerateReporters(getter_AddRefs(e));
+    nsRefPtr<MemoryReportsWrapper> wrappedReports =
+        new MemoryReportsWrapper(&reports);
+    nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback(process);
     bool more;
     while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
       nsCOMPtr<nsIMemoryReporter> r;
       e->GetNext(getter_AddRefs(r));
-
-      nsCString path;
-      int32_t kind;
-      int32_t units;
-      int64_t amount;
-      nsCString desc;
-
-      if (NS_SUCCEEDED(r->GetPath(path)) &&
-          NS_SUCCEEDED(r->GetKind(&kind)) &&
-          NS_SUCCEEDED(r->GetUnits(&units)) &&
-          NS_SUCCEEDED(r->GetAmount(&amount)) &&
-          NS_SUCCEEDED(r->GetDescription(desc)))
-      {
-        MemoryReport memreport(process, path, kind, units, amount, desc);
-        reports.AppendElement(memreport);
-      }
-    }
-
-    // Then do the memory multi-reporters, by calling CollectReports on each
-    // one, whereupon the callback will turn each measurement into a
-    // MemoryReport.
-    mgr->EnumerateMultiReporters(getter_AddRefs(e));
-    nsRefPtr<MemoryReportsWrapper> wrappedReports =
-        new MemoryReportsWrapper(&reports);
-    nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback(process);
-    while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
-      nsCOMPtr<nsIMemoryMultiReporter> r;
-      e->GetNext(getter_AddRefs(r));
       r->CollectReports(cb, wrappedReports);
     }
 
     child->Send__delete__(child, reports);
     return true;
 }
 
 bool
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -156,44 +156,64 @@ using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 
 namespace mozilla {
 namespace dom {
 
 #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
 
-// This represents a single measurement taken by a memory reporter in a child
-// process and passed to this one.  Its process is non-empty, and its amount is
-// fixed.
-class ChildMemoryReporter MOZ_FINAL : public MemoryReporterBase
+// This represents all the memory reports provided by a child process.
+class ChildReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
-    ChildMemoryReporter(const char* aProcess, const char* aPath, int32_t aKind,
-                        int32_t aUnits, int64_t aAmount,
-                        const char* aDescription)
-      : MemoryReporterBase(aPath, aKind, aUnits, aDescription)
-      , mProcess(aProcess)
-      , mAmount(aAmount)
+    ChildReporter(const InfallibleTArray<MemoryReport>& childReports)
     {
+        for (uint32_t i = 0; i < childReports.Length(); i++) {
+            MemoryReport r(childReports[i].process(),
+                           childReports[i].path(),
+                           childReports[i].kind(),
+                           childReports[i].units(),
+                           childReports[i].amount(),
+                           childReports[i].desc());
+
+            // Child reports have a non-empty process.
+            MOZ_ASSERT(!r.process().IsEmpty());
+
+            mChildReports.AppendElement(r);
+        }
     }
 
-    NS_IMETHOD GetProcess(nsACString& aProcess)
+    NS_DECL_ISUPPORTS
+
+    NS_IMETHOD GetName(nsACString& name)
     {
-      aProcess.Assign(mProcess);
-      return NS_OK;
+        name.AssignLiteral("content-child");
+        return NS_OK;
     }
 
-private:
-    int64_t Amount() { return mAmount; }
-
-    nsCString mProcess;
-    int64_t   mAmount;
+    NS_IMETHOD CollectReports(nsIMemoryReporterCallback* aCb,
+                              nsISupports* aClosure)
+    {
+        for (uint32_t i = 0; i < mChildReports.Length(); i++) {
+            nsresult rv;
+            MemoryReport r = mChildReports[i];
+            rv = aCb->Callback(r.process(), r.path(), r.kind(), r.units(),
+                               r.amount(), r.desc(), aClosure);
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
+        return NS_OK;
+    }
+
+  private:
+    InfallibleTArray<MemoryReport> mChildReports;
 };
 
+NS_IMPL_ISUPPORTS1(ChildReporter, nsIMemoryReporter)
+
 class MemoryReportRequestParent : public PMemoryReportRequestParent
 {
 public:
     MemoryReportRequestParent();
     virtual ~MemoryReportRequestParent();
 
     virtual bool Recv__delete__(const InfallibleTArray<MemoryReport>& report);
 private:
@@ -204,49 +224,49 @@ private:
 };
 
 MemoryReportRequestParent::MemoryReportRequestParent()
 {
     MOZ_COUNT_CTOR(MemoryReportRequestParent);
 }
 
 bool
-MemoryReportRequestParent::Recv__delete__(const InfallibleTArray<MemoryReport>& report)
+MemoryReportRequestParent::Recv__delete__(const InfallibleTArray<MemoryReport>& childReports)
 {
-    Owner()->SetChildMemoryReporters(report);
+    Owner()->SetChildMemoryReports(childReports);
     return true;
 }
 
 MemoryReportRequestParent::~MemoryReportRequestParent()
 {
     MOZ_COUNT_DTOR(MemoryReportRequestParent);
 }
 
 /**
  * A memory reporter for ContentParent objects themselves.
  */
-class ContentParentMemoryReporter MOZ_FINAL : public nsIMemoryMultiReporter
+class ContentParentMemoryReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
     NS_DECL_ISUPPORTS
-    NS_DECL_NSIMEMORYMULTIREPORTER
+    NS_DECL_NSIMEMORYREPORTER
     NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MallocSizeOf)
 };
 
-NS_IMPL_ISUPPORTS1(ContentParentMemoryReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(ContentParentMemoryReporter, nsIMemoryReporter)
 
 NS_IMETHODIMP
 ContentParentMemoryReporter::GetName(nsACString& aName)
 {
     aName.AssignLiteral("ContentParents");
     return NS_OK;
 }
 
 NS_IMETHODIMP
-ContentParentMemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* cb,
+ContentParentMemoryReporter::CollectReports(nsIMemoryReporterCallback* cb,
                                             nsISupports* aClosure)
 {
     nsAutoTArray<ContentParent*, 16> cps;
     ContentParent::GetAllEvenIfDead(cps);
 
     for (uint32_t i = 0; i < cps.Length(); i++) {
         ContentParent* cp = cps[i];
         AsyncChannel* channel = cp->GetIPCChannel();
@@ -353,17 +373,17 @@ ContentParent::MaybeTakePreallocatedAppP
 /*static*/ void
 ContentParent::StartUp()
 {
     if (XRE_GetProcessType() != GeckoProcessType_Default) {
         return;
     }
 
     nsRefPtr<ContentParentMemoryReporter> mr = new ContentParentMemoryReporter();
-    NS_RegisterMemoryMultiReporter(mr);
+    NS_RegisterMemoryReporter(mr);
 
     sCanLaunchSubprocesses = true;
 
     // Try to preallocate a process that we can transform into an app later.
     PreallocatedProcessManager::AllocateAfterDelay();
 }
 
 /*static*/ void
@@ -872,17 +892,17 @@ ContentParent::ShutDownProcess(bool aClo
     // NB: must MarkAsDead() here so that this isn't accidentally
     // returned from Get*() while in the midst of shutdown.
     MarkAsDead();
 
     // A ContentParent object might not get freed until after XPCOM shutdown has
     // shut down the cycle collector.  But by then it's too late to release any
     // CC'ed objects, so we need to null them out here, while we still can.  See
     // bug 899761.
-    mMemoryReporters.Clear();
+    mChildReporter = nullptr;
     if (mMessageManager) {
       mMessageManager->Disconnect();
       mMessageManager = nullptr;
     }
 }
 
 void
 ContentParent::MarkAsDead()
@@ -1023,18 +1043,18 @@ ContentParent::ActorDestroy(ActorDestroy
         obs->RemoveObserver(static_cast<nsIObserver*>(this), "a11y-init-or-shutdown");
 #endif
     }
 
     if (ppm) {
       ppm->Disconnect();
     }
 
-    // clear the child memory reporters
-    ClearChildMemoryReporters();
+    // unregister the child memory reporter
+    UnregisterChildMemoryReporter();
 
     // remove the global remote preferences observers
     Preferences::RemoveObserver(this, "");
 
     RecvRemoveGeolocationListener();
 
     mConsoleService = nullptr;
 
@@ -2136,52 +2156,39 @@ ContentParent::AllocPMemoryReportRequest
 bool
 ContentParent::DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor)
 {
   delete actor;
   return true;
 }
 
 void
-ContentParent::SetChildMemoryReporters(const InfallibleTArray<MemoryReport>& report)
+ContentParent::SetChildMemoryReports(const InfallibleTArray<MemoryReport>& childReports)
 {
     nsCOMPtr<nsIMemoryReporterManager> mgr =
         do_GetService("@mozilla.org/memory-reporter-manager;1");
-    for (int32_t i = 0; i < mMemoryReporters.Count(); i++)
-        mgr->UnregisterReporter(mMemoryReporters[i]);
-
-    for (uint32_t i = 0; i < report.Length(); i++) {
-        nsCString process  = report[i].process();
-        nsCString path     = report[i].path();
-        int32_t   kind     = report[i].kind();
-        int32_t   units    = report[i].units();
-        int64_t   amount   = report[i].amount();
-        nsCString desc     = report[i].desc();
-
-        nsRefPtr<ChildMemoryReporter> r =
-            new ChildMemoryReporter(process.get(), path.get(), kind, units,
-                                    amount, desc.get());
-
-        mMemoryReporters.AppendObject(r);
-        mgr->RegisterReporter(r);
-    }
+
+    if (mChildReporter)
+        mgr->UnregisterReporter(mChildReporter);
+
+    mChildReporter = new ChildReporter(childReports);
+    mgr->RegisterReporter(mChildReporter);
 
     nsCOMPtr<nsIObserverService> obs =
         do_GetService("@mozilla.org/observer-service;1");
     if (obs)
         obs->NotifyObservers(nullptr, "child-memory-reporter-update", nullptr);
 }
 
 void
-ContentParent::ClearChildMemoryReporters()
+ContentParent::UnregisterChildMemoryReporter()
 {
     nsCOMPtr<nsIMemoryReporterManager> mgr =
         do_GetService("@mozilla.org/memory-reporter-manager;1");
-    for (int32_t i = 0; i < mMemoryReporters.Count(); i++)
-        mgr->UnregisterReporter(mMemoryReporters[i]);
+    mgr->UnregisterReporter(mChildReporter);
 }
 
 PTestShellParent*
 ContentParent::AllocPTestShellParent()
 {
   return new TestShellParent();
 }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -138,18 +138,19 @@ public:
     jsipc::JavaScriptParent *GetCPOWManager();
 
     void ReportChildAlreadyBlocked();
     bool RequestRunToCompletion();
 
     bool IsAlive();
     bool IsForApp();
 
-    void SetChildMemoryReporters(const InfallibleTArray<MemoryReport>& report);
-    void ClearChildMemoryReporters();
+    void SetChildMemoryReports(const InfallibleTArray<MemoryReport>&
+                               childReports);
+    void UnregisterChildMemoryReporter();
 
     GeckoChildProcessHost* Process() {
         return mSubprocess;
     }
 
     int32_t Pid();
 
     bool NeedsPermissionsUpdate() {
@@ -444,21 +445,23 @@ private:
     // details.
 
     GeckoChildProcessHost* mSubprocess;
     base::ChildPrivileges mOSPrivileges;
 
     uint64_t mChildID;
     int32_t mGeolocationWatchID;
 
-    // This is a cache of all of the memory reporters
-    // registered in the child process.  To update this, one
-    // can broadcast the topic "child-memory-reporter-request" using
-    // the nsIObserverService.
-    nsCOMArray<nsIMemoryReporter> mMemoryReporters;
+    // This is a reporter holding the reports from the child's last
+    // "child-memory-reporter-update" notification.  To update this, one can
+    // broadcast the topic "child-memory-reporter-request" using the
+    // nsIObserverService.
+    //
+    // Note that this assumes there is at most one child process at a time!
+    nsCOMPtr<nsIMemoryReporter> mChildReporter;
 
     nsString mAppManifestURL;
 
     /**
      * We cache mAppName instead of looking it up using mAppManifestURL when we
      * need it because it turns out that getting an app from the apps service is
      * expensive.
      */
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1762,17 +1762,17 @@ struct WorkerPrivate::TimeoutInfo
   mozilla::TimeDuration mInterval;
   nsCString mFilename;
   uint32_t mLineNumber;
   uint32_t mId;
   bool mIsInterval;
   bool mCanceled;
 };
 
-class WorkerPrivate::MemoryReporter MOZ_FINAL : public nsIMemoryMultiReporter
+class WorkerPrivate::MemoryReporter MOZ_FINAL : public nsIMemoryReporter
 {
   friend class WorkerPrivate;
 
   SharedMutex mMutex;
   WorkerPrivate* mWorkerPrivate;
   nsCString mRtPath;
   bool mAlreadyMappedToAddon;
 
@@ -1803,17 +1803,17 @@ public:
   NS_IMETHOD
   GetName(nsACString& aName)
   {
     aName.AssignLiteral("workers");
     return NS_OK;
   }
 
   NS_IMETHOD
-  CollectReports(nsIMemoryMultiReporterCallback* aCallback,
+  CollectReports(nsIMemoryReporterCallback* aCallback,
                  nsISupports* aClosure)
   {
     AssertIsOnMainThread();
 
     // Assumes that WorkerJSRuntimeStats will hold a reference to mRtPath,
     // and not a copy, as TryToMapAddon() may later modify the string again.
     WorkerJSRuntimeStats rtStats(mRtPath);
 
@@ -1879,17 +1879,17 @@ private:
 
     static const size_t explicitLength = strlen("explicit/");
     addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0);
     addonId += "/";
     mRtPath.Insert(addonId, explicitLength);
   }
 };
 
-NS_IMPL_ISUPPORTS1(WorkerPrivate::MemoryReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
 
 template <class Derived>
 WorkerPrivateParent<Derived>::WorkerPrivateParent(
                                      JSContext* aCx,
                                      JS::Handle<JSObject*> aObject,
                                      WorkerPrivate* aParent,
                                      JSContext* aParentJSContext,
                                      const nsAString& aScriptURL,
@@ -3089,17 +3089,17 @@ void
 WorkerPrivate::EnableMemoryReporter()
 {
   AssertIsOnWorkerThread();
 
   // No need to lock here since the main thread can't race until we've
   // successfully registered the reporter.
   mMemoryReporter = new MemoryReporter(this);
 
-  if (NS_FAILED(NS_RegisterMemoryMultiReporter(mMemoryReporter))) {
+  if (NS_FAILED(NS_RegisterMemoryReporter(mMemoryReporter))) {
     NS_WARNING("Failed to register memory reporter!");
     // No need to lock here since a failed registration means our memory
     // reporter can't start running. Just clean up.
     mMemoryReporter = nullptr;
 
     return;
   }
 }
@@ -3144,17 +3144,17 @@ WorkerPrivate::DisableMemoryReporter()
       }
 
       NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!");
       mBlockedForMemoryReporter = false;
     }
   }
 
   // Finally unregister the memory reporter.
-  if (NS_FAILED(NS_UnregisterMemoryMultiReporter(memoryReporter))) {
+  if (NS_FAILED(NS_UnregisterMemoryReporter(memoryReporter))) {
     NS_WARNING("Failed to unregister memory reporter!");
   }
 }
 
 void
 WorkerPrivate::WaitForWorkerEvents(PRIntervalTime aInterval)
 {
   AssertIsOnWorkerThread();
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -29,17 +29,16 @@
 
 #include "EventTarget.h"
 #include "Queue.h"
 #include "WorkerFeature.h"
 
 class JSAutoStructuredCloneBuffer;
 class nsIChannel;
 class nsIDocument;
-class nsIMemoryMultiReporter;
 class nsIPrincipal;
 class nsIScriptContext;
 class nsIURI;
 class nsPIDOMWindow;
 class nsITimer;
 
 namespace JS {
 class RuntimeStats;
--- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp
@@ -92,21 +92,21 @@ NS_INTERFACE_MAP_BEGIN(mozHunspell)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozHunspell)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_3(mozHunspell,
                            mPersonalDictionary,
                            mEncoder,
                            mDecoder)
 
-class SpellCheckReporter MOZ_FINAL : public mozilla::MemoryReporterBase
+class SpellCheckReporter MOZ_FINAL : public mozilla::MemoryUniReporter
 {
 public:
   SpellCheckReporter()
-    : MemoryReporterBase("explicit/spell-check", KIND_HEAP, UNITS_BYTES,
+    : MemoryUniReporter("explicit/spell-check", KIND_HEAP, UNITS_BYTES,
 "Memory used by the Hunspell spell checking engine's internal data structures.")
   {
 #ifdef DEBUG
     // There must be only one instance of this class, due to |sAmount|
     // being static.
     static bool hasRun = false;
     MOZ_ASSERT(!hasRun);
     hasRun = true;
--- a/gfx/gl/GfxTexturesReporter.h
+++ b/gfx/gl/GfxTexturesReporter.h
@@ -8,22 +8,22 @@
 #define GFXTEXTURESREPORTER_H_
 
 #include "nsIMemoryReporter.h"
 #include "GLTypes.h"
 
 namespace mozilla {
 namespace gl {
 
-class GfxTexturesReporter MOZ_FINAL : public MemoryReporterBase
+class GfxTexturesReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     GfxTexturesReporter()
-      : MemoryReporterBase("gfx-textures", KIND_OTHER, UNITS_BYTES,
-                           "Memory used for storing GL textures.")
+      : MemoryUniReporter("gfx-textures", KIND_OTHER, UNITS_BYTES,
+                          "Memory used for storing GL textures.")
     {
 #ifdef DEBUG
         // There must be only one instance of this class, due to |sAmount|
         // being static.  Assert this.
         static bool hasRun = false;
         MOZ_ASSERT(!hasRun);
         hasRun = true;
 #endif
--- a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp
+++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp
@@ -179,23 +179,23 @@ PixelFormatForContentType(gfxASurface::g
 }
 
 static gfxASurface::gfxContentType
 ContentTypeFromPixelFormat(android::PixelFormat aFormat)
 {
   return gfxASurface::ContentFromFormat(ImageFormatForPixelFormat(aFormat));
 }
 
-class GrallocReporter MOZ_FINAL : public MemoryReporterBase
+class GrallocReporter MOZ_FINAL : public MemoryUniReporter
 {
   friend class GrallocBufferActor;
 
 public:
   GrallocReporter()
-    : MemoryReporterBase("gralloc", KIND_OTHER, UNITS_BYTES,
+    : MemoryUniReporter("gralloc", KIND_OTHER, UNITS_BYTES,
 "Special RAM that can be shared between processes and directly accessed by "
 "both the CPU and GPU.  Gralloc memory is usually a relatively precious "
 "resource, with much less available than generic RAM.  When it's exhausted, "
 "graphics performance can suffer. This value can be incorrect because of race "
 "conditions.")
   {
 #ifdef DEBUG
     // There must be only one instance of this class, due to |sAmount|
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -593,31 +593,31 @@ PR_STATIC_ASSERT(uint32_t(CAIRO_SURFACE_
 PR_STATIC_ASSERT(uint32_t(CAIRO_SURFACE_TYPE_SKIA) ==
                  uint32_t(gfxASurface::SurfaceTypeSkia));
 
 /* Surface size memory reporting */
 
 static int64_t gSurfaceMemoryUsed[gfxASurface::SurfaceTypeMax] = { 0 };
 
 class SurfaceMemoryReporter MOZ_FINAL :
-    public nsIMemoryMultiReporter
+    public nsIMemoryReporter
 {
 public:
     SurfaceMemoryReporter()
     { }
 
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD GetName(nsACString &name)
     {
         name.AssignLiteral("gfx-surface");
         return NS_OK;
     }
 
-    NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCb,
+    NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCb,
                               nsISupports *aClosure)
     {
         size_t len = NS_ARRAY_LENGTH(sSurfaceMemoryReporterAttrs);
         for (size_t i = 0; i < len; i++) {
             int64_t amount = gSurfaceMemoryUsed[i];
 
             if (amount != 0) {
                 const char *path = sSurfaceMemoryReporterAttrs[i].path;
@@ -634,30 +634,30 @@ public:
                 NS_ENSURE_SUCCESS(rv, rv);
             }
         }
 
         return NS_OK;
     }
 };
 
-NS_IMPL_ISUPPORTS1(SurfaceMemoryReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(SurfaceMemoryReporter, nsIMemoryReporter)
 
 void
 gfxASurface::RecordMemoryUsedForSurfaceType(gfxASurface::gfxSurfaceType aType,
                                             int32_t aBytes)
 {
     if (aType < 0 || aType >= SurfaceTypeMax) {
         NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
         return;
     }
 
     static bool registered = false;
     if (!registered) {
-        NS_RegisterMemoryMultiReporter(new SurfaceMemoryReporter());
+        NS_RegisterMemoryReporter(new SurfaceMemoryReporter());
         registered = true;
     }
 
     gSurfaceMemoryUsed[aType] += aBytes;
 }
 
 void
 gfxASurface::RecordMemoryUsed(int32_t aBytes)
--- a/gfx/thebes/gfxAndroidPlatform.cpp
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -25,22 +25,22 @@
 #include FT_MODULE_H
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 static FT_Library gPlatformFTLibrary = nullptr;
 
-class FreetypeReporter MOZ_FINAL : public MemoryReporterBase
+class FreetypeReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     FreetypeReporter()
-      : MemoryReporterBase("explicit/freetype", KIND_HEAP, UNITS_BYTES,
-                           "Memory used by Freetype.")
+      : MemoryUniReporter("explicit/freetype", KIND_HEAP, UNITS_BYTES,
+                          "Memory used by Freetype.")
     {
 #ifdef DEBUG
         // There must be only one instance of this class, due to |sAmount|
         // being static.
         static bool hasRun = false;
         MOZ_ASSERT(!hasRun);
         hasRun = true;
 #endif
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1248,30 +1248,30 @@ gfxFontFamily::SizeOfIncludingThis(Mallo
 /*
  * gfxFontCache - global cache of gfxFont instances.
  * Expires unused fonts after a short interval;
  * notifies fonts to age their cached shaped-word records;
  * observes memory-pressure notification and tells fonts to clear their
  * shaped-word caches to free up memory.
  */
 
-NS_IMPL_ISUPPORTS1(gfxFontCache::MemoryReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(gfxFontCache::MemoryReporter, nsIMemoryReporter)
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(FontCacheMallocSizeOf)
 
 NS_IMETHODIMP
 gfxFontCache::MemoryReporter::GetName(nsACString &aName)
 {
     aName.AssignLiteral("font-cache");
     return NS_OK;
 }
 
 NS_IMETHODIMP
 gfxFontCache::MemoryReporter::CollectReports
-    (nsIMemoryMultiReporterCallback* aCb,
+    (nsIMemoryReporterCallback* aCb,
      nsISupports* aClosure)
 {
     FontCacheSizes sizes;
 
     gfxFontCache::GetCache()->SizeOfIncludingThis(&FontCacheMallocSizeOf,
                                                   &sizes);
 
     aCb->Callback(EmptyCString(),
@@ -1320,17 +1320,17 @@ MemoryPressureObserver::Observe(nsISuppo
 nsresult
 gfxFontCache::Init()
 {
     NS_ASSERTION(!gGlobalCache, "Where did this come from?");
     gGlobalCache = new gfxFontCache();
     if (!gGlobalCache) {
         return NS_ERROR_OUT_OF_MEMORY;
     }
-    NS_RegisterMemoryMultiReporter(new MemoryReporter);
+    NS_RegisterMemoryReporter(new MemoryReporter);
     return NS_OK;
 }
 
 void
 gfxFontCache::Shutdown()
 {
     delete gGlobalCache;
     gGlobalCache = nullptr;
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -972,21 +972,21 @@ public:
 
     void SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                              FontCacheSizes*   aSizes) const;
     void SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                              FontCacheSizes*   aSizes) const;
 
 protected:
     class MemoryReporter MOZ_FINAL
-        : public nsIMemoryMultiReporter
+        : public nsIMemoryReporter
     {
     public:
         NS_DECL_ISUPPORTS
-        NS_DECL_NSIMEMORYMULTIREPORTER
+        NS_DECL_NSIMEMORYREPORTER
     };
 
     void DestroyFont(gfxFont *aFont);
 
     static gfxFontCache *gGlobalCache;
 
     struct Key {
         const gfxFontEntry* mFontEntry;
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -65,30 +65,30 @@ gfxFontListPrefObserver::Observe(nsISupp
     NS_ASSERTION(!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID), "invalid topic");
     // XXX this could be made to only clear out the cache for the prefs that were changed
     // but it probably isn't that big a deal.
     gfxPlatformFontList::PlatformFontList()->ClearPrefFonts();
     gfxFontCache::GetCache()->AgeAllGenerations();
     return NS_OK;
 }
 
-NS_IMPL_ISUPPORTS1(gfxPlatformFontList::MemoryReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter)
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(FontListMallocSizeOf)
 
 NS_IMETHODIMP
 gfxPlatformFontList::MemoryReporter::GetName(nsACString &aName)
 {
     aName.AssignLiteral("font-list");
     return NS_OK;
 }
 
 NS_IMETHODIMP
 gfxPlatformFontList::MemoryReporter::CollectReports
-    (nsIMemoryMultiReporterCallback* aCb,
+    (nsIMemoryReporterCallback* aCb,
      nsISupports* aClosure)
 {
     FontListSizes sizes;
     sizes.mFontListSize = 0;
     sizes.mFontTableCacheSize = 0;
     sizes.mCharMapsSize = 0;
 
     gfxPlatformFontList::PlatformFontList()->SizeOfIncludingThis(&FontListMallocSizeOf,
@@ -136,17 +136,17 @@ gfxPlatformFontList::gfxPlatformFontList
 
     // 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);
 
-    NS_RegisterMemoryMultiReporter(new MemoryReporter);
+    NS_RegisterMemoryReporter(new MemoryReporter);
 }
 
 gfxPlatformFontList::~gfxPlatformFontList()
 {
     mSharedCmaps.Clear();
     NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
     Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs);
     NS_RELEASE(gFontListPrefObserver);
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -174,21 +174,21 @@ public:
     // 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 MOZ_FINAL
-        : public nsIMemoryMultiReporter
+        : public nsIMemoryReporter
     {
     public:
         NS_DECL_ISUPPORTS
-        NS_DECL_NSIMEMORYMULTIREPORTER
+        NS_DECL_NSIMEMORYREPORTER
     };
 
     gfxPlatformFontList(bool aNeedFullnamePostscriptNames = true);
 
     static gfxPlatformFontList *sPlatformFontList;
 
     static PLDHashOperator FindFontForCharProc(nsStringHashKey::KeyType aKey,
                                                nsRefPtr<gfxFontFamily>& aFamilyEntry,
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -68,21 +68,21 @@ using namespace mozilla;
 #ifdef CAIRO_HAS_D2D_SURFACE
 
 static const char *kFeatureLevelPref =
   "gfx.direct3d.last_used_feature_level_idx";
 static const int kSupportedFeatureLevels[] =
   { D3D10_FEATURE_LEVEL_10_1, D3D10_FEATURE_LEVEL_10_0,
     D3D10_FEATURE_LEVEL_9_3 };
 
-class GfxD2DSurfaceCacheReporter MOZ_FINAL : public MemoryReporterBase
+class GfxD2DSurfaceCacheReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     GfxD2DSurfaceCacheReporter()
-      : MemoryReporterBase("gfx-d2d-surface-cache", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("gfx-d2d-surface-cache", KIND_OTHER, UNITS_BYTES,
 "Memory used by the Direct2D internal surface cache.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
         return cairo_d2d_get_image_surface_cache_usage();
     }
 };
@@ -105,52 +105,52 @@ bool OncePreferenceDirect2DForceEnabled(
   if (preferenceValue < 0) {
     preferenceValue = Preferences::GetBool("gfx.direct2d.force-enabled", false);
   }
   return !!preferenceValue;
 }
 
 } // anonymous namespace
 
-class GfxD2DSurfaceVramReporter MOZ_FINAL : public MemoryReporterBase
+class GfxD2DSurfaceVramReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     GfxD2DSurfaceVramReporter()
-      : MemoryReporterBase("gfx-d2d-surface-vram", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("gfx-d2d-surface-vram", KIND_OTHER, UNITS_BYTES,
                            "Video memory used by D2D surfaces.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE {
       cairo_device_t *device =
           gfxWindowsPlatform::GetPlatform()->GetD2DDevice();
       return device ? cairo_d2d_get_surface_vram_usage(device) : 0;
     }
 };
 
 #endif
 
-class GfxD2DVramDrawTargetReporter MOZ_FINAL : public MemoryReporterBase
+class GfxD2DVramDrawTargetReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     GfxD2DVramDrawTargetReporter()
-      : MemoryReporterBase("gfx-d2d-vram-draw-target", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("gfx-d2d-vram-draw-target", KIND_OTHER, UNITS_BYTES,
                            "Video memory used by D2D DrawTargets.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
         return Factory::GetD2DVRAMUsageDrawTarget();
     }
 };
 
-class GfxD2DVramSourceSurfaceReporter MOZ_FINAL : public MemoryReporterBase
+class GfxD2DVramSourceSurfaceReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     GfxD2DVramSourceSurfaceReporter()
-      : MemoryReporterBase("gfx-d2d-vram-source-surface",
+      : MemoryUniReporter("gfx-d2d-vram-source-surface",
                            KIND_OTHER, UNITS_BYTES,
                            "Video memory used by D2D SourceSurfaces.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
         return Factory::GetD2DVRAMUsageSourceSurface();
     }
@@ -201,17 +201,17 @@ typedef HRESULT (WINAPI*D3D11CreateDevic
   D3D_FEATURE_LEVEL *pFeatureLevels,
   UINT FeatureLevels,
   UINT SDKVersion,
   ID3D11Device **ppDevice,
   D3D_FEATURE_LEVEL *pFeatureLevel,
   ID3D11DeviceContext *ppImmediateContext
 );
 
-class GPUAdapterMultiReporter : public nsIMemoryMultiReporter {
+class GPUAdapterReporter : public nsIMemoryReporter {
 
     // Callers must Release the DXGIAdapter after use or risk mem-leak
     static bool GetDXGIAdapter(IDXGIAdapter **DXGIAdapter)
     {
         ID3D10Device1 *D2D10Device;
         IDXGIDevice *DXGIDevice;
         bool result = false;
         
@@ -223,27 +223,27 @@ class GPUAdapterMultiReporter : public n
         }
         
         return result;
     }
     
 public:
     NS_DECL_ISUPPORTS
 
-    // nsIMemoryMultiReporter abstract method implementation
+    // nsIMemoryReporter abstract method implementation
     NS_IMETHOD
     GetName(nsACString &aName)
     {
         aName.AssignLiteral("gpuadapter");
         return NS_OK;
     }
     
-    // nsIMemoryMultiReporter abstract method implementation
+    // nsIMemoryReporter abstract method implementation
     NS_IMETHOD
-    CollectReports(nsIMemoryMultiReporterCallback* aCb,
+    CollectReports(nsIMemoryReporterCallback* aCb,
                    nsISupports* aClosure)
     {
         int32_t winVers, buildNum;
         HANDLE ProcessHandle = GetCurrentProcess();
         
         int64_t dedicatedBytesUsed = 0;
         int64_t sharedBytesUsed = 0;
         int64_t committedBytesUsed = 0;
@@ -342,17 +342,17 @@ public:
         REPORT("gpu-shared", sharedBytesUsed,
                "In-process memory that is shared with the GPU.");
         
 #undef REPORT
 
         return NS_OK;
     }
 };
-NS_IMPL_ISUPPORTS1(GPUAdapterMultiReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(GPUAdapterReporter, nsIMemoryReporter)
 
 static __inline void
 BuildKeyNameFromFontName(nsAString &aName)
 {
     if (aName.Length() >= LF_FACESIZE)
         aName.Truncate(LF_FACESIZE - 1);
     ToLowerCase(aName);
 }
@@ -379,26 +379,26 @@ gfxWindowsPlatform::gfxWindowsPlatform()
     NS_RegisterMemoryReporter(new GfxD2DSurfaceVramReporter());
     mD2DDevice = nullptr;
 #endif
     NS_RegisterMemoryReporter(new GfxD2DVramDrawTargetReporter());
     NS_RegisterMemoryReporter(new GfxD2DVramSourceSurfaceReporter());
 
     UpdateRenderMode();
 
-    mGPUAdapterMultiReporter = new GPUAdapterMultiReporter();
-    NS_RegisterMemoryMultiReporter(mGPUAdapterMultiReporter);
+    mGPUAdapterReporter = new GPUAdapterReporter();
+    NS_RegisterMemoryReporter(mGPUAdapterReporter);
 }
 
 gfxWindowsPlatform::~gfxWindowsPlatform()
 {
-    NS_UnregisterMemoryMultiReporter(mGPUAdapterMultiReporter);
-    
-     mDeviceManager = nullptr;
-     
+    NS_UnregisterMemoryReporter(mGPUAdapterReporter);
+
+    mDeviceManager = nullptr;
+
     ::ReleaseDC(nullptr, mScreenDC);
     // not calling FT_Done_FreeType because cairo may still hold references to
     // these FT_Faces.  See bug 458169.
 #ifdef CAIRO_HAS_D2D_SURFACE
     if (mD2DDevice) {
         cairo_release_device(mD2DDevice);
     }
 #endif
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -47,17 +47,17 @@ namespace mozilla {
 namespace layers {
 class DeviceManagerD3D9;
 }
 }
 class IDirect3DDevice9;
 class ID3D11Device;
 class IDXGIAdapter1;
 
-class nsIMemoryMultiReporter;
+class nsIMemoryReporter;
 
 // Utility to get a Windows HDC from a thebes context,
 // used by both GDI and Uniscribe font shapers
 struct DCFromContext {
     DCFromContext(gfxContext *aContext) {
         dc = nullptr;
         nsRefPtr<gfxASurface> aSurface = aContext->CurrentSurface();
         NS_ASSERTION(aSurface || !aContext->IsCairo(), "DCFromContext: null surface");
@@ -310,12 +310,12 @@ private:
     bool mD3D9DeviceInitialized;
     bool mD3D11DeviceInitialized;
 
     virtual qcms_profile* GetPlatformCMSOutputProfile();
 
     // TODO: unify this with mPrefFonts (NB: holds families, not fonts) in gfxPlatformFontList
     nsDataHashtable<nsCStringHashKey, nsTArray<nsRefPtr<gfxFontEntry> > > mPrefFonts;
 
-    nsIMemoryMultiReporter* mGPUAdapterMultiReporter;
+    nsIMemoryReporter* mGPUAdapterReporter;
 };
 
 #endif /* GFX_WINDOWS_PLATFORM_H */
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -44,32 +44,32 @@
 #include "nsILoadGroupChild.h"
 
 using namespace mozilla;
 using namespace mozilla::image;
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(ImagesMallocSizeOf)
 
 class imgMemoryReporter MOZ_FINAL :
-  public nsIMemoryMultiReporter
+  public nsIMemoryReporter
 {
 public:
   imgMemoryReporter()
   {
   }
 
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD GetName(nsACString &name)
   {
     name.Assign("images");
     return NS_OK;
   }
 
-  NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *callback,
+  NS_IMETHOD CollectReports(nsIMemoryReporterCallback *callback,
                             nsISupports *closure)
   {
     AllSizes chrome;
     AllSizes content;
 
     for (uint32_t i = 0; i < mKnownLoaders.Length(); i++) {
       mKnownLoaders[i]->mChromeCache.EnumerateRead(EntryAllSizes, &chrome);
       mKnownLoaders[i]->mCache.EnumerateRead(EntryAllSizes, &content);
@@ -216,25 +216,25 @@ private:
         *n += image->NonHeapSizeOfDecoded();
       }
     }
 
     return PL_DHASH_NEXT;
   }
 };
 
-NS_IMPL_ISUPPORTS1(imgMemoryReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(imgMemoryReporter, nsIMemoryReporter)
 
 // This is used by telemetry.
 class ImagesContentUsedUncompressedReporter MOZ_FINAL
-  : public MemoryReporterBase
+  : public MemoryUniReporter
 {
 public:
   ImagesContentUsedUncompressedReporter()
-    : MemoryReporterBase("images-content-used-uncompressed",
+    : MemoryUniReporter("images-content-used-uncompressed",
                          KIND_OTHER, UNITS_BYTES,
 "This is the sum of the 'explicit/images/content/used/uncompressed-heap' "
 "and 'explicit/images/content/used/uncompressed-nonheap' numbers.  However, "
 "it is measured at a different time and so may give slightly different "
 "results.")
   {}
 private:
   int64_t Amount() MOZ_OVERRIDE
@@ -833,17 +833,17 @@ void imgLoader::GlobalInit()
   int32_t cachesize;
   rv = Preferences::GetInt("image.cache.size", &cachesize);
   if (NS_SUCCEEDED(rv))
     sCacheMaxSize = cachesize;
   else
     sCacheMaxSize = 5 * 1024 * 1024;
 
   sMemReporter = new imgMemoryReporter();
-  NS_RegisterMemoryMultiReporter(sMemReporter);
+  NS_RegisterMemoryReporter(sMemReporter);
   NS_RegisterMemoryReporter(new ImagesContentUsedUncompressedReporter());
 }
 
 nsresult imgLoader::InitCache()
 {
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (!os)
     return NS_ERROR_FAILURE;
--- a/image/test/mochitest/test_bug601470.html
+++ b/image/test/mochitest/test_bug601470.html
@@ -20,24 +20,28 @@ https://bugzilla.mozilla.org/show_bug.cg
 /** Test for Bug 601470 **/
 
 SimpleTest.waitForExplicitFinish();
 
 window.onload = function() {
   var mgr = SpecialPowers.Cc["@mozilla.org/memory-reporter-manager;1"]
     .getService(SpecialPowers.Ci.nsIMemoryReporterManager);
 
+  var amount = 0;
+  var handleReport = function(aProcess, aPath, aKind, aUnits, aAmount, aDesc) {
+    amount += aAmount;
+  }
+
   var e = mgr.enumerateReporters();
-  var memoryCounter = 0;
   while (e.hasMoreElements()) {
-    var mr =
-      e.getNext().QueryInterface(SpecialPowers.Ci.nsIMemoryReporter);
-    memoryCounter += mr.amount;
+    var mr = e.getNext().QueryInterface(SpecialPowers.Ci.nsIMemoryReporter);
+    mr.collectReports(handleReport, null);
   }
-  ok(memoryCounter > 0, "we should be using a nonzero amount of memory");
+
+  ok(amount > 0, "we should be using a nonzero amount of memory");
   ok(true, "yay, didn't crash!");
 
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body>
--- a/ipc/glue/SharedMemory.cpp
+++ b/ipc/glue/SharedMemory.cpp
@@ -12,33 +12,33 @@
 #include "mozilla/Atomics.h"
 
 namespace mozilla {
 namespace ipc {
 
 static Atomic<size_t> gShmemAllocated;
 static Atomic<size_t> gShmemMapped;
 
-class ShmemAllocatedReporter MOZ_FINAL : public MemoryReporterBase
+class ShmemAllocatedReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   ShmemAllocatedReporter()
-    : MemoryReporterBase("shmem-allocated", KIND_OTHER, UNITS_BYTES,
+    : MemoryUniReporter("shmem-allocated", KIND_OTHER, UNITS_BYTES,
 "Memory shared with other processes that is accessible (but not necessarily "
 "mapped).")
   {}
 private:
   int64_t Amount() MOZ_OVERRIDE { return gShmemAllocated; }
 };
 
-class ShmemMappedReporter MOZ_FINAL : public MemoryReporterBase
+class ShmemMappedReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   ShmemMappedReporter()
-    : MemoryReporterBase("shmem-mapped", KIND_OTHER, UNITS_BYTES,
+    : MemoryUniReporter("shmem-mapped", KIND_OTHER, UNITS_BYTES,
 "Memory shared with other processes that is mapped into the address space.")
   {}
 private:
   int64_t Amount() MOZ_OVERRIDE { return gShmemMapped; }
 };
 
 SharedMemory::SharedMemory()
   : mAllocSize(0)
--- a/js/xpconnect/src/XPCJSMemoryReporter.h
+++ b/js/xpconnect/src/XPCJSMemoryReporter.h
@@ -4,30 +4,30 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef XPCJSMemoryReporter_h
 #define XPCJSMemoryReporter_h
 
 class nsISupports;
-class nsIMemoryMultiReporterCallback;
+class nsIMemoryReporterCallback;
 
 namespace xpc {
 
 // The key is the window ID.
 typedef nsDataHashtable<nsUint64HashKey, nsCString> WindowPaths;
 
-// This is very nearly an instance of nsIMemoryMultiReporter, but it's not,
+// This is very nearly an instance of nsIMemoryReporter, but it's not,
 // because it's invoked by nsWindowMemoryReporter in order to get |windowPaths|
 // in CollectReports.
-class JSMemoryMultiReporter
+class JSReporter
 {
 public:
     static nsresult CollectReports(WindowPaths *windowPaths,
                                    WindowPaths *topWindowPaths,
-                                   nsIMemoryMultiReporterCallback *cb,
+                                   nsIMemoryReporterCallback *cb,
                                    nsISupports *closure);
 };
 
 }
 
 #endif
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1556,84 +1556,82 @@ GetCompartmentName(JSCompartment *c, nsC
         // (such as about:memory) have to undo this change.
         if (replaceSlashes)
             name.ReplaceChar('/', '\\');
     } else {
         name.AssignLiteral("null-principal");
     }
 }
 
-// Telemetry relies on this being a single reporter (rather than part of the
-// "js" multi-reporter).
-class JSGCHeapReporter MOZ_FINAL : public MemoryReporterBase
+// Telemetry relies on this being a uni-reporter (rather than part of the "js"
+// reporter).
+class JSGCHeapReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     JSGCHeapReporter()
-      : MemoryReporterBase("js-gc-heap", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("js-gc-heap", KIND_OTHER, UNITS_BYTES,
 "Memory used by the garbage-collected JavaScript heap.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
         JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
         return int64_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) *
                js::gc::ChunkSize;
     }
 };
 
-// Nb: js-compartments/system + js-compartments/user could be
-// different to the number of compartments reported by
-// JSMemoryMultiReporter if a garbage collection occurred
-// between them being consulted.  We could move these reporters into
-// XPConnectJSCompartmentCount to avoid that problem, but then we couldn't
-// easily report them via telemetry, so we live with the small risk of
-// inconsistencies.
-
-class JSCompartmentsSystemReporter MOZ_FINAL : public MemoryReporterBase
+// Nb: js-compartments/system + js-compartments/user could be different to the
+// number of compartments reported by JSReporter if a garbage collection
+// occurred between them being consulted.  We could move these reporters into
+// JSReporter to avoid that problem, but then we couldn't easily report them
+// via telemetry, so we live with the small risk of inconsistencies.
+
+class JSCompartmentsSystemReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     JSCompartmentsSystemReporter()
-      : MemoryReporterBase("js-compartments/system", KIND_OTHER, UNITS_COUNT,
+      : MemoryUniReporter("js-compartments/system", KIND_OTHER, UNITS_COUNT,
 "The number of JavaScript compartments for system code.  The sum of this and "
 "'js-compartments/user' might not match the number of compartments listed "
 "in the 'explicit' tree if a garbage collection occurs at an inopportune "
 "time, but such cases should be rare.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
         JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
         return JS::SystemCompartmentCount(rt);
     }
 };
 
-class JSCompartmentsUserReporter MOZ_FINAL : public MemoryReporterBase
+class JSCompartmentsUserReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     JSCompartmentsUserReporter()
-      : MemoryReporterBase("js-compartments/user", KIND_OTHER, UNITS_COUNT,
+      : MemoryUniReporter("js-compartments/user", KIND_OTHER, UNITS_COUNT,
 "The number of JavaScript compartments for user code.  The sum of this and "
 "'js-compartments/system' might not match the number of compartments listed "
 "under 'js' if a garbage collection occurs at an inopportune time, but such "
 "cases should be rare.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
         JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
         return JS::UserCompartmentCount(rt);
     }
 };
 
 // This is also a single reporter so it can be used by telemetry.
-class JSMainRuntimeTemporaryPeakReporter MOZ_FINAL : public MemoryReporterBase
+class JSMainRuntimeTemporaryPeakReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     JSMainRuntimeTemporaryPeakReporter()
-      : MemoryReporterBase("js-main-runtime-temporary-peak",
+      : MemoryUniReporter("js-main-runtime-temporary-peak",
                            KIND_OTHER, UNITS_BYTES,
 "The peak size of the transient storage in the main JSRuntime (the current "
 "size of which is reported as 'explicit/js-non-window/runtime/temporary').")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
         JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime();
@@ -1754,17 +1752,17 @@ private:
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf)
 
 namespace xpc {
 
 static nsresult
 ReportZoneStats(const JS::ZoneStats &zStats,
                 const xpc::ZoneStatsExtras &extras,
-                nsIMemoryMultiReporterCallback *cb,
+                nsIMemoryReporterCallback *cb,
                 nsISupports *closure, size_t *gcTotalOut = NULL)
 {
     const nsAutoCString& pathPrefix = extras.pathPrefix;
     size_t gcTotal = 0, gcHeapSundries = 0, otherSundries = 0;
 
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
                       zStats.gcHeapArenaAdmin,
                       "Memory on the garbage-collected JavaScript "
@@ -1938,17 +1936,17 @@ ReportZoneStats(const JS::ZoneStats &zSt
 
 #   undef STRING_LENGTH
 }
 
 static nsresult
 ReportCompartmentStats(const JS::CompartmentStats &cStats,
                        const xpc::CompartmentStatsExtras &extras,
                        amIAddonManager *addonManager,
-                       nsIMemoryMultiReporterCallback *cb,
+                       nsIMemoryReporterCallback *cb,
                        nsISupports *closure, size_t *gcTotalOut = NULL)
 {
     static const nsDependentCString addonPrefix("explicit/add-ons/");
 
     size_t gcTotal = 0, gcHeapSundries = 0, otherSundries = 0;
     nsAutoCString cJSPathPrefix = extras.jsPathPrefix;
     nsAutoCString cDOMPathPrefix = extras.domPathPrefix;
 
@@ -2080,18 +2078,18 @@ ReportCompartmentStats(const JS::Compart
                    cStats.objectsExtra.propertyIteratorData,
                    "Memory allocated on the malloc heap for data belonging to property iterator objects.");
 
     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("objects/malloc-heap/ctypes-data"),
                    cStats.objectsExtra.ctypesData,
                    "Memory allocated on the malloc heap for data belonging to ctypes objects.");
 
     // Note that we use cDOMPathPrefix here.  This is because we measure orphan
-    // DOM nodes in the JS multi-reporter, but we want to report them in a
-    // "dom" sub-tree rather than a "js" sub-tree.
+    // DOM nodes in the JS reporter, but we want to report them in a "dom"
+    // sub-tree rather than a "js" sub-tree.
     ZCREPORT_BYTES(cDOMPathPrefix + NS_LITERAL_CSTRING("orphan-nodes"),
                    cStats.objectsExtra.private_,
                    "Memory used by orphan DOM nodes that are only reachable "
                    "from JavaScript objects.");
 
     ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"),
                    cStats.shapesExtraTreeTables,
                    "Memory allocated on the malloc heap for the property tables "
@@ -2203,17 +2201,17 @@ ReportCompartmentStats(const JS::Compart
 
     return NS_OK;
 }
 
 static nsresult
 ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
                                  const nsACString &rtPath,
                                  amIAddonManager* addonManager,
-                                 nsIMemoryMultiReporterCallback *cb,
+                                 nsIMemoryReporterCallback *cb,
                                  nsISupports *closure, size_t *rtTotalOut)
 {
     nsresult rv;
 
     size_t gcTotal = 0;
 
     for (size_t i = 0; i < rtStats.zoneStatsVector.length(); i++) {
         const JS::ZoneStats &zStats = rtStats.zoneStatsVector[i];
@@ -2345,29 +2343,29 @@ ReportJSRuntimeExplicitTreeStats(const J
     MOZ_ASSERT(gcTotal == rtStats.gcHeapChunkTotal);
 
     return NS_OK;
 }
 
 nsresult
 ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
                                  const nsACString &rtPath,
-                                 nsIMemoryMultiReporterCallback *cb,
+                                 nsIMemoryReporterCallback *cb,
                                  nsISupports *closure, size_t *rtTotalOut)
 {
     nsCOMPtr<amIAddonManager> am =
       do_GetService("@mozilla.org/addons/integration;1");
     return ReportJSRuntimeExplicitTreeStats(rtStats, rtPath, am.get(), cb,
                                             closure, rtTotalOut);
 }
 
 
 } // namespace xpc
 
-class JSCompartmentsMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter
+class JSCompartmentsReporter MOZ_FINAL : public nsIMemoryReporter
 {
   public:
     NS_DECL_THREADSAFE_ISUPPORTS
 
     NS_IMETHOD GetName(nsACString &name) {
         name.AssignLiteral("compartments");
         return NS_OK;
     }
@@ -2381,17 +2379,17 @@ class JSCompartmentsMultiReporter MOZ_FI
         GetCompartmentName(c, path, true);
         path.Insert(js::IsSystemCompartment(c)
                     ? NS_LITERAL_CSTRING("compartments/system/")
                     : NS_LITERAL_CSTRING("compartments/user/"),
                     0);
         paths->append(path);
     }
 
-    NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *cb,
+    NS_IMETHOD CollectReports(nsIMemoryReporterCallback *cb,
                               nsISupports *closure)
     {
         // First we collect the compartment paths.  Then we report them.  Doing
         // the two steps interleaved is a bad idea, because calling |cb|
         // from within CompartmentCallback() leads to all manner of assertions.
 
         // Collect.
 
@@ -2403,19 +2401,17 @@ class JSCompartmentsMultiReporter MOZ_FI
         for (size_t i = 0; i < paths.length(); i++)
             // These ones don't need a description, hence the "".
             REPORT(nsCString(paths[i]), KIND_OTHER, UNITS_COUNT, 1, "");
 
         return NS_OK;
     }
 };
 
-NS_IMPL_ISUPPORTS1(JSCompartmentsMultiReporter
-                              , nsIMemoryMultiReporter
-                              )
+NS_IMPL_ISUPPORTS1(JSCompartmentsReporter, nsIMemoryReporter)
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(OrphanMallocSizeOf)
 
 namespace xpc {
 
 static size_t
 SizeOfTreeIncludingThis(nsINode *tree)
 {
@@ -2562,32 +2558,32 @@ class XPCJSRuntimeStats : public JS::Run
 
         extras->jsPathPrefix += NS_LITERAL_CSTRING("compartment(") + cName + NS_LITERAL_CSTRING(")/");
 
         // extras->jsPathPrefix is used for almost all the compartment-specific
         // reports. At this point it has the form
         // "<something>compartment(<cname>)/".
         //
         // extras->domPathPrefix is used for DOM orphan nodes, which are
-        // counted by the JS multi-reporter but reported as part of the
-        // DOM measurements. At this point it has the form "<something>/dom/"
-        // if this compartment belongs to an nsGlobalWindow, and
+        // counted by the JS reporter but reported as part of the DOM
+        // measurements. At this point it has the form "<something>/dom/" if
+        // this compartment belongs to an nsGlobalWindow, and
         // "explicit/dom/<something>?!/" otherwise (in which case it shouldn't
         // be used, because non-nsGlobalWindow compartments shouldn't have
         // orphan DOM nodes).
 
         cstats->extra = extras;
     }
 };
 
 nsresult
-JSMemoryMultiReporter::CollectReports(WindowPaths *windowPaths,
-                                      WindowPaths *topWindowPaths,
-                                      nsIMemoryMultiReporterCallback *cb,
-                                      nsISupports *closure)
+JSReporter::CollectReports(WindowPaths *windowPaths,
+                           WindowPaths *topWindowPaths,
+                           nsIMemoryReporterCallback *cb,
+                           nsISupports *closure)
 {
     XPCJSRuntime *xpcrt = nsXPConnect::GetRuntimeInstance();
 
     // In the first step we get all the stats and stash them in a local
     // data structure.  In the second step we pass all the stashed stats to
     // the callback.  Separating these steps is important because the
     // callback may be a JS function, and executing JS while getting these
     // stats seems like a bad idea.
@@ -3047,17 +3043,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
     // internationalization APIs work as desired.
     if (!xpc_LocalizeRuntime(runtime))
         NS_RUNTIMEABORT("xpc_LocalizeRuntime failed.");
 
     NS_RegisterMemoryReporter(new JSGCHeapReporter());
     NS_RegisterMemoryReporter(new JSCompartmentsSystemReporter());
     NS_RegisterMemoryReporter(new JSCompartmentsUserReporter());
     NS_RegisterMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
-    NS_RegisterMemoryMultiReporter(new JSCompartmentsMultiReporter);
+    NS_RegisterMemoryReporter(new JSCompartmentsReporter);
 
     // Install a JavaScript 'debugger' keyword handler in debug builds only
 #ifdef DEBUG
     if (!JS_GetGlobalDebugHooks(runtime)->debuggerHandler)
         xpc_InstallJSDebuggerKeywordHandler(runtime);
 #endif
 }
 
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -158,17 +158,17 @@ xpc_TryUnmarkWrappedGrayObject(nsISuppor
 extern void
 xpc_UnmarkSkippableJSHolders();
 
 // No JS can be on the stack when this is called. Probably only useful from
 // xpcshell.
 NS_EXPORT_(void)
 xpc_ActivateDebugMode();
 
-class nsIMemoryMultiReporterCallback;
+class nsIMemoryReporterCallback;
 
 // readable string conversions, static methods and members only
 class XPCStringConvert
 {
 public:
 
     // If the string shares the readable's buffer, that buffer will
     // get assigned to *sharedBuffer.  Otherwise null will be
@@ -354,17 +354,17 @@ private:
 
 // This reports all the stats in |rtStats| that belong in the "explicit" tree,
 // (which isn't all of them).
 // @see ZoneStatsExtras
 // @see CompartmentStatsExtras
 nsresult
 ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
                                  const nsACString &rtPath,
-                                 nsIMemoryMultiReporterCallback *cb,
+                                 nsIMemoryReporterCallback *cb,
                                  nsISupports *closure, size_t *rtTotal = NULL);
 
 /**
  * Throws an exception on cx and returns false.
  */
 bool
 Throw(JSContext *cx, nsresult rv);
 
--- a/layout/base/nsStyleSheetService.cpp
+++ b/layout/base/nsStyleSheetService.cpp
@@ -21,21 +21,21 @@
 #include "nsNetUtil.h"
 #include "nsIObserverService.h"
 #include "nsLayoutStatics.h"
 #include "nsIMemoryReporter.h"
 
 using namespace mozilla;
 
 class LayoutStyleSheetServiceReporter MOZ_FINAL
-  : public mozilla::MemoryReporterBase
+  : public mozilla::MemoryUniReporter
 {
 public:
   LayoutStyleSheetServiceReporter()
-    : MemoryReporterBase("explicit/layout/style-sheet-service",
+    : MemoryUniReporter("explicit/layout/style-sheet-service",
                          KIND_HEAP, UNITS_BYTES,
 "Memory used for style sheets held by the style sheet service.")
   {}
 private:
   int64_t Amount() MOZ_OVERRIDE
   {
     return nsStyleSheetService::gInstance
          ? nsStyleSheetService::gInstance->SizeOfIncludingThis(MallocSizeOf)
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -13,21 +13,21 @@
 #include "nsIMemoryReporter.h"
 #include "nsNetUtil.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIXULRuntime.h"
 #include "nsCSSStyleSheet.h"
 
 class LayoutStyleSheetCacheReporter MOZ_FINAL
-  : public mozilla::MemoryReporterBase
+  : public mozilla::MemoryUniReporter
 {
 public:
   LayoutStyleSheetCacheReporter()
-    : MemoryReporterBase("explicit/layout/style-sheet-cache",
+    : MemoryUniReporter("explicit/layout/style-sheet-cache",
                          KIND_HEAP, UNITS_BYTES,
                          "Memory used for some built-in style sheets.")
   {}
 private:
   int64_t Amount() MOZ_OVERRIDE
   {
     return nsLayoutStylesheetCache::SizeOfIncludingThis(MallocSizeOf);
   }
--- a/modules/libpref/src/Preferences.cpp
+++ b/modules/libpref/src/Preferences.cpp
@@ -198,21 +198,21 @@ Preferences::SizeOfIncludingThisAndOther
                                              aMallocSizeOf);
   }
   // We don't measure sRootBranch and sDefaultRootBranch here because
   // DMD indicates they are not significant.
   n += pref_SizeOfPrivateData(aMallocSizeOf);
   return n;
 }
 
-class PreferencesReporter MOZ_FINAL : public MemoryReporterBase
+class PreferencesReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   PreferencesReporter()
-    : MemoryReporterBase("explicit/preferences", KIND_HEAP, UNITS_BYTES,
+    : MemoryUniReporter("explicit/preferences", KIND_HEAP, UNITS_BYTES,
                          "Memory used by the preferences system.")
   {}
 private:
   int64_t Amount() MOZ_OVERRIDE
   {
     return Preferences::SizeOfIncludingThisAndOtherStuff(MallocSizeOf);
   }
 };
--- a/netwerk/cache/nsDiskCacheDevice.cpp
+++ b/netwerk/cache/nsDiskCacheDevice.cpp
@@ -365,21 +365,21 @@ nsDiskCache::Truncate(PRFileDesc *  fd, 
     return NS_OK;
 }
 
 
 /******************************************************************************
  *  nsDiskCacheDevice
  *****************************************************************************/
 
-class NetworkDiskCacheReporter MOZ_FINAL : public MemoryReporterBase
+class NetworkDiskCacheReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     NetworkDiskCacheReporter(nsDiskCacheDevice* aDevice)
-      : MemoryReporterBase(
+      : MemoryUniReporter(
             "explicit/network/disk-cache",
             KIND_HEAP,
             UNITS_BYTES,
             "Memory used by the network disk cache.")
       , mDevice(aDevice)
     {}
 
 private:
--- a/netwerk/cache/nsMemoryCacheDevice.cpp
+++ b/netwerk/cache/nsMemoryCacheDevice.cpp
@@ -24,21 +24,21 @@
 // The queues hold exponentially increasing ranges of floor(log2((size/nref)))
 // values for entries.
 // Entries larger than 2^(kQueueCount-1) go in the last queue.
 // Entries with no expiration go in the first queue.
 
 const char *gMemoryDeviceID      = "memory";
 
 class NetworkMemoryCacheReporter MOZ_FINAL :
-    public mozilla::MemoryReporterBase
+    public mozilla::MemoryUniReporter
 {
 public:
     NetworkMemoryCacheReporter(nsMemoryCacheDevice* aDevice)
-      : MemoryReporterBase(
+      : MemoryUniReporter(
             "explicit/network/memory-cache",
             KIND_HEAP,
             UNITS_BYTES,
             "Memory used by the network memory cache.")
       , mDevice(aDevice)
     {}
 
 private:
--- a/netwerk/dns/nsEffectiveTLDService.cpp
+++ b/netwerk/dns/nsEffectiveTLDService.cpp
@@ -56,21 +56,21 @@ nsDomainEntry::FuncForStaticAsserts(void
 #undef ETLD_ENTRY_OFFSET
 #undef ETLD_STR_NUM
 #undef ETLD_STR_NUM1
 
 // ----------------------------------------------------------------------
 
 static nsEffectiveTLDService *gService = nullptr;
 
-class EffectiveTLDServiceReporter MOZ_FINAL : public MemoryReporterBase
+class EffectiveTLDServiceReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   EffectiveTLDServiceReporter()
-    : MemoryReporterBase("explicit/xpcom/effective-TLD-service",
+    : MemoryUniReporter("explicit/xpcom/effective-TLD-service",
                          KIND_HEAP, UNITS_BYTES,
                          "Memory used by the effective TLD service.")
   {}
 
 private:
   int64_t Amount() MOZ_OVERRIDE
   {
     return gService ? gService->SizeOfIncludingThis(MallocSizeOf) : 0;
--- a/startupcache/StartupCache.cpp
+++ b/startupcache/StartupCache.cpp
@@ -48,39 +48,39 @@
 #define SC_WORDSIZE "4"
 #else
 #define SC_WORDSIZE "8"
 #endif
 
 namespace mozilla {
 namespace scache {
 
-class StartupCacheMappingReporter MOZ_FINAL : public MemoryReporterBase
+class StartupCacheMappingReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   StartupCacheMappingReporter()
-    : MemoryReporterBase("explicit/startup-cache/mapping",
+    : MemoryUniReporter("explicit/startup-cache/mapping",
                          KIND_NONHEAP, UNITS_BYTES,
 "Memory used to hold the mapping of the startup cache from file.  This memory "
 "is likely to be swapped out shortly after start-up.")
   {}
 private:
   int64_t Amount() MOZ_OVERRIDE
   {
     mozilla::scache::StartupCache* sc =
       mozilla::scache::StartupCache::GetSingleton();
     return sc ? sc->SizeOfMapping() : 0;
   }
 };
 
-class StartupCacheDataReporter MOZ_FINAL : public MemoryReporterBase
+class StartupCacheDataReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   StartupCacheDataReporter()
-    : MemoryReporterBase("explicit/startup-cache/data", KIND_HEAP, UNITS_BYTES,
+    : MemoryUniReporter("explicit/startup-cache/data", KIND_HEAP, UNITS_BYTES,
 "Memory used by the startup cache for things other than the file mapping.")
   {}
 private:
   int64_t Amount() MOZ_OVERRIDE
   {
     mozilla::scache::StartupCache* sc =
       mozilla::scache::StartupCache::GetSingleton();
     return sc ? sc->HeapSizeOfIncludingThis(MallocSizeOf) : 0;
--- a/storage/src/mozStorageService.cpp
+++ b/storage/src/mozStorageService.cpp
@@ -52,28 +52,28 @@ namespace storage {
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Memory Reporting
 
 // We don't need an "explicit" reporter for total SQLite memory usage, because
 // the multi-reporter provides reports that add up to the total.  But it's
 // useful to have the total in the "Other Measurements" list in about:memory,
 // and more importantly, we also gather the total via telemetry.
-class StorageSQLiteReporter MOZ_FINAL : public MemoryReporterBase
+class StorageSQLiteUniReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
-  StorageSQLiteReporter()
-    : MemoryReporterBase("storage-sqlite", KIND_OTHER, UNITS_BYTES,
+  StorageSQLiteUniReporter()
+    : MemoryUniReporter("storage-sqlite", KIND_OTHER, UNITS_BYTES,
                          "Memory used by SQLite.")
   {}
 private:
   int64_t Amount() MOZ_OVERRIDE { return ::sqlite3_memory_used(); }
 };
 
-class StorageSQLiteMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter
+class StorageSQLiteMultiReporter MOZ_FINAL : public nsIMemoryReporter
 {
 private:
   Service *mService;    // a weakref because Service contains a strongref to this
   nsCString mStmtDesc;
   nsCString mCacheDesc;
   nsCString mSchemaDesc;
 
 public:
@@ -92,27 +92,27 @@ public:
 
     mSchemaDesc = NS_LITERAL_CSTRING(
       "Memory (approximate) used to store the schema for all databases "
       "associated with connections to this database.");
   }
 
   NS_IMETHOD GetName(nsACString &aName)
   {
-      aName.AssignLiteral("storage-sqlite");
+      aName.AssignLiteral("storage-sqlite-multi");
       return NS_OK;
   }
 
   // Warning: To get a Connection's measurements requires holding its lock.
   // There may be a delay getting the lock if another thread is accessing the
   // Connection.  This isn't very nice if CollectReports is called from the
   // main thread!  But at the time of writing this function is only called when
   // about:memory is loaded (not, for example, when telemetry pings occur) and
   // any delays in that case aren't so bad.
-  NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCb,
+  NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCb,
                             nsISupports *aClosure)
   {
     nsresult rv;
     size_t totalConnSize = 0;
     {
       nsTArray<nsRefPtr<Connection> > connections;
       mService->getConnections(connections);
 
@@ -159,18 +159,17 @@ public:
                        aClosure);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 
 private:
   /**
-   * Passes a single SQLite memory statistic to a memory multi-reporter
-   * callback.
+   * Passes a single SQLite memory statistic to a memory reporter callback.
    *
    * @param aCallback
    *        The callback.
    * @param aClosure
    *        The closure for the callback.
    * @param aConn
    *        The SQLite connection.
    * @param aPathHead
@@ -180,17 +179,17 @@ private:
    *        "schema".
    * @param aDesc
    *        The memory report description.
    * @param aOption
    *        The SQLite constant for getting the measurement.
    * @param aTotal
    *        The accumulator for the measurement.
    */
-  nsresult reportConn(nsIMemoryMultiReporterCallback *aCb,
+  nsresult reportConn(nsIMemoryReporterCallback *aCb,
                       nsISupports *aClosure,
                       sqlite3 *aConn,
                       const nsACString &aPathHead,
                       const nsACString &aKind,
                       const nsACString &aDesc,
                       int aOption,
                       size_t *aTotal)
   {
@@ -211,17 +210,17 @@ private:
     *aTotal += curr;
 
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS1(
   StorageSQLiteMultiReporter,
-  nsIMemoryMultiReporter
+  nsIMemoryReporter
 )
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Service
 
 NS_IMPL_ISUPPORTS2(
   Service,
   mozIStorageService,
@@ -303,18 +302,18 @@ Service::Service()
 , mSqliteVFS(nullptr)
 , mRegistrationMutex("Service::mRegistrationMutex")
 , mConnections()
 {
 }
 
 Service::~Service()
 {
-  (void)::NS_UnregisterMemoryReporter(mStorageSQLiteReporter);
-  (void)::NS_UnregisterMemoryMultiReporter(mStorageSQLiteMultiReporter);
+  (void)::NS_UnregisterMemoryReporter(mStorageSQLiteUniReporter);
+  (void)::NS_UnregisterMemoryReporter(mStorageSQLiteMultiReporter);
 
   int rc = sqlite3_vfs_unregister(mSqliteVFS);
   if (rc != SQLITE_OK)
     NS_WARNING("Failed to unregister sqlite vfs wrapper.");
 
   // Shutdown the sqlite3 API.  Warn if shutdown did not turn out okay, but
   // there is nothing actionable we can do in that case.
   rc = ::sqlite3_shutdown();
@@ -536,20 +535,20 @@ Service::initialize()
   // We need to obtain the toolkit.storage.pageSize preferences on the main
   // thread because the preference service can only be accessed there.  This
   // is cached in the service for all future Open[Unshared]Database calls.
   sDefaultPageSize =
       Preferences::GetInt(PREF_TS_PAGESIZE, PREF_TS_PAGESIZE_DEFAULT);
 
   // Create and register our SQLite memory reporters.  Registration can only
   // happen on the main thread (otherwise you'll get cryptic crashes).
-  mStorageSQLiteReporter = new StorageSQLiteReporter();
+  mStorageSQLiteUniReporter = new StorageSQLiteUniReporter();
   mStorageSQLiteMultiReporter = new StorageSQLiteMultiReporter(this);
-  (void)::NS_RegisterMemoryReporter(mStorageSQLiteReporter);
-  (void)::NS_RegisterMemoryMultiReporter(mStorageSQLiteMultiReporter);
+  (void)::NS_RegisterMemoryReporter(mStorageSQLiteUniReporter);
+  (void)::NS_RegisterMemoryReporter(mStorageSQLiteMultiReporter);
 
   return NS_OK;
 }
 
 int
 Service::localeCompareStrings(const nsAString &aStr1,
                               const nsAString &aStr2,
                               int32_t aComparisonStrength)
--- a/storage/src/mozStorageService.h
+++ b/storage/src/mozStorageService.h
@@ -12,17 +12,16 @@
 #include "nsIFile.h"
 #include "nsIObserver.h"
 #include "nsTArray.h"
 #include "mozilla/Mutex.h"
 
 #include "mozIStorageService.h"
 
 class nsIMemoryReporter;
-class nsIMemoryMultiReporter;
 class nsIXPConnect;
 struct sqlite3_vfs;
 
 namespace mozilla {
 namespace storage {
 
 class Connection;
 class Service : public mozIStorageService
@@ -168,18 +167,18 @@ private:
    *
    * @note Collation implementations are platform-dependent and in general not
    *       thread-safe.  Access to this collation should be synchronized.
    */
   nsCOMPtr<nsICollation> mLocaleCollation;
 
   nsCOMPtr<nsIFile> mProfileStorageFile;
 
-  nsCOMPtr<nsIMemoryReporter> mStorageSQLiteReporter;
-  nsCOMPtr<nsIMemoryMultiReporter> mStorageSQLiteMultiReporter;
+  nsCOMPtr<nsIMemoryReporter> mStorageSQLiteUniReporter;
+  nsCOMPtr<nsIMemoryReporter> mStorageSQLiteMultiReporter;
 
   static Service *gService;
 
   static nsIXPConnect *sXPConnect;
 
   static int32_t sSynchronousPref;
   static int32_t sDefaultPageSize;
 };
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -161,82 +161,65 @@ function onUnload()
                .getService(Ci.nsIObserverService);
     os.removeObserver(gChildMemoryListener, "child-memory-reporter-update");
   }
 }
 
 //---------------------------------------------------------------------------
 
 /**
- * Iterates over each reporter and multi-reporter.
+ * Iterates over each reporter.
  *
- * @param aIgnoreSingle
- *        Function that indicates if we should skip a single reporter, based
- *        on its path.
- * @param aIgnoreMulti
- *        Function that indicates if we should skip a multi-reporter, based on
- *        its name.
+ * @param aIgnoreReporter
+ *        Function that indicates if we should skip an entire reporter, based
+ *        on its name.
+ * @param aIgnoreReport
+ *        Function that indicates if we should skip a single report from a
+ *        reporter, based on its path.
  * @param aHandleReport
- *        The function that's called for each report.
+ *        The function that's called for each non-skipped report.
  */
-function processMemoryReporters(aIgnoreSingle, aIgnoreMulti, aHandleReport)
+function processMemoryReporters(aIgnoreReporter, aIgnoreReport, aHandleReport)
 {
-  // Process each memory reporter with aHandleReport.
-  //
-  // - Note that copying rOrig.amount (which calls a C++ function under the
-  //   IDL covers) to r._amount for every reporter now means that the
-  //   results as consistent as possible -- measurements are made all at
-  //   once before most of the memory required to generate this page is
-  //   allocated.
-  //
-  // - After this point we never use the original memory report again.
+  let handleReport = function(aProcess, aUnsafePath, aKind, aUnits,
+                              aAmount, aDescription) {
+    if (!aIgnoreReport(aUnsafePath)) {
+      aHandleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
+                    aDescription, /* presence = */ undefined);
+    }
+  }
 
   let e = gMgr.enumerateReporters();
   while (e.hasMoreElements()) {
-    let rOrig = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
-    let unsafePath = rOrig.path;
-    if (!aIgnoreSingle(unsafePath)) {
-      aHandleReport(rOrig.process, unsafePath, rOrig.kind, rOrig.units,
-                    rOrig.amount, rOrig.description);
-    }
-  }
-
-  let e = gMgr.enumerateMultiReporters();
-  while (e.hasMoreElements()) {
-    let mr = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
-    if (!aIgnoreMulti(mr.name)) {
+    let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
+    if (!aIgnoreReporter(mr.name)) {
       // |collectReports| never passes in a |presence| argument.
-      let handleReport = function(aProcess, aUnsafePath, aKind, aUnits,
-                                  aAmount, aDescription) {
-        aHandleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
-                      aDescription, /* presence = */ undefined);
-      }
       mr.collectReports(handleReport, null);
     }
   }
 }
 
 /**
  * Iterates over each report.
  *
  * @param aReports
  *        Array of reports, read from a file or the clipboard.
- * @param aIgnoreSingle
- *        Function that indicates if we should skip a single reporter, based
+ * @param aIgnoreReport
+ *        Function that indicates if we should skip a single report, based
  *        on its path.
  * @param aHandleReport
  *        The function that's called for each report.
  */
-function processMemoryReportsFromFile(aReports, aIgnoreSingle, aHandleReport)
+function processMemoryReportsFromFile(aReports, aIgnoreReport, aHandleReport)
 {
   // Process each memory reporter with aHandleReport.
 
   for (let i = 0; i < aReports.length; i++) {
     let r = aReports[i];
-    if (!aIgnoreSingle(r.path)) {
+    if (!aIgnoreReport(r.path)) {
       aHandleReport(r.process, r.path, r.kind, r.units, r.amount,
                     r.description, r._presence);
     }
   }
 }
 
 //---------------------------------------------------------------------------
 
@@ -586,20 +569,17 @@ function updateAboutMemoryFromReporters(
 {
   // First, clear the contents of main.  Necessary because
   // updateAboutMemoryFromReporters() might be called more than once due to the
   // "child-memory-reporter-update" observer.
   updateMainAndFooter("", SHOW_FOOTER);
 
   try {
     // Process the reports from the memory reporters.
-    let process = function(aIgnoreSingle, aIgnoreMulti, aHandleReport) {
-      processMemoryReporters(aIgnoreSingle, aIgnoreMulti, aHandleReport);
-    }
-    appendAboutMemoryMain(process, gMgr.hasMozMallocUsableSize,
+    appendAboutMemoryMain(processMemoryReporters, gMgr.hasMozMallocUsableSize,
                           /* forceShowSmaps = */ false);
 
   } catch (ex) {
     handleException(ex);
   }
 }
 
 // Increment this if the JSON format changes.
@@ -616,18 +596,18 @@ function updateAboutMemoryFromJSONObject
 {
   try {
     assertInput(aObj.version === gCurrentFileFormatVersion,
                 "data version number missing or doesn't match");
     assertInput(aObj.hasMozMallocUsableSize !== undefined,
                 "missing 'hasMozMallocUsableSize' property");
     assertInput(aObj.reports && aObj.reports instanceof Array,
                 "missing or non-array 'reports' property");
-    let process = function(aIgnoreSingle, aIgnoreMulti, aHandleReport) {
-      processMemoryReportsFromFile(aObj.reports, aIgnoreSingle, aHandleReport);
+    let process = function(aIgnoreReporter, aIgnoreReport, aHandleReport) {
+      processMemoryReportsFromFile(aObj.reports, aIgnoreReport, aHandleReport);
     }
     appendAboutMemoryMain(process, aObj.hasMozMallocUsableSize,
                           /* forceShowSmaps = */ true);
   } catch (ex) {
     handleException(ex);
   }
 }
 
@@ -968,29 +948,29 @@ function PColl()
   this._degenerates = {};
   this._heapTotal = 0;
 }
 
 /**
  * Processes reports (whether from reporters or from a file) and append the
  * main part of the page.
  *
- * @param aProcess
+ * @param aProcessReports
  *        Function that extracts the memory reports from the reporters or from
  *        file.
  * @param aHasMozMallocUsableSize
  *        Boolean indicating if moz_malloc_usable_size works.
  * @param aForceShowSmaps
  *        True if we should show the smaps memory reporters even if we're not
  *        in verbose mode.
  */
-function appendAboutMemoryMain(aProcess, aHasMozMallocUsableSize,
+function appendAboutMemoryMain(aProcessReports, aHasMozMallocUsableSize,
                                aForceShowSmaps)
 {
-  let pcollsByProcess = getPCollsByProcess(aProcess, aForceShowSmaps);
+  let pcollsByProcess = getPCollsByProcess(aProcessReports, aForceShowSmaps);
 
   // Sort the processes.
   let processes = Object.keys(pcollsByProcess);
   processes.sort(function(aProcessA, aProcessB) {
     assert(aProcessA != aProcessB,
            "Elements of Object.keys() should be unique, but " +
            "saw duplicate '" + aProcessA + "' elem.");
 
@@ -1038,64 +1018,62 @@ function appendAboutMemoryMain(aProcess,
                                      aHasMozMallocUsableSize);
   }
 }
 
 /**
  * This function reads all the memory reports, and puts that data in structures
  * that will be used to generate the page.
  *
- * @param aProcessMemoryReports
+ * @param aProcessReports
  *        Function that extracts the memory reports from the reporters or from
  *        file.
  * @param aForceShowSmaps
  *        True if we should show the smaps memory reporters even if we're not
  *        in verbose mode.
  * @return The table of PColls by process.
  */
-function getPCollsByProcess(aProcessMemoryReports, aForceShowSmaps)
+function getPCollsByProcess(aProcessReports, aForceShowSmaps)
 {
   let pcollsByProcess = {};
 
   // This regexp matches sentences and sentence fragments, i.e. strings that
   // start with a capital letter and ends with a '.'.  (The final sentence may
   // be in parentheses, so a ')' might appear after the '.'.)
   const gSentenceRegExp = /^[A-Z].*\.\)?$/m;
 
-  // Ignore the "smaps" multi-reporter in non-verbose mode unless we're reading
-  // from a file or the clipboard, and ignore the "compartments" and
-  // "ghost-windows" multi-reporters all the time.  (Note that reports from
-  // these multi-reporters can reach here as single reports if they were in the
-  // child process.)
+  // Ignore the "smaps" reporter in non-verbose mode unless we're reading from
+  // a file or the clipboard, and ignore the "compartments" and "ghost-windows"
+  // reporters all the time.  (Note that reports from these reporters can reach
+  // here via a "content-child" reporter if they were in a child process.)
   //
-  // Also ignore the resident-fast reporter; we use the vanilla resident
+  // Also ignore the "resident-fast" reporter; we use the vanilla "resident"
   // reporter because it's more important that we get accurate results than
   // that we avoid the (small) possibility of a long pause when loading
-  // about:memory.
-  //
-  // We don't show both resident and resident-fast because running the resident
-  // reporter can purge pages on MacOS, which affects the results of the
-  // resident-fast reporter.  We don't want about:memory's results to be
-  // affected by the order of memory reporter execution.
+  // about:memory.  Furthermore, the "resident" reporter can purge pages on
+  // MacOS, which affects the results of the "resident-fast" reporter, and we
+  // don't want the measurements shown in about:memory to be affected by the
+  // (arbitrary) order of memory reporter execution.
 
-  function ignoreSingle(aUnsafePath)
+  function ignoreReporter(aName)
+  {
+    return (aName === "smaps" && !gVerbose.checked && !aForceShowSmaps) ||
+           aName === "compartments" ||
+           aName === "ghost-windows" ||
+           aName === "resident-fast";
+  }
+
+  function ignoreReport(aUnsafePath)
   {
     return (isSmapsPath(aUnsafePath) && !gVerbose.checked && !aForceShowSmaps) ||
            aUnsafePath.startsWith("compartments/") ||
            aUnsafePath.startsWith("ghost-windows/") ||
            aUnsafePath == "resident-fast";
   }
 
-  function ignoreMulti(aMRName)
-  {
-    return (aMRName === "smaps" && !gVerbose.checked && !aForceShowSmaps) ||
-            aMRName === "compartments" ||
-            aMRName === "ghost-windows";
-  }
-
   function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
                         aDescription, aPresence)
   {
     if (isExplicitPath(aUnsafePath)) {
       assertInput(aKind === KIND_HEAP || aKind === KIND_NONHEAP,
                   "bad explicit kind");
       assertInput(aUnits === UNITS_BYTES, "bad explicit units");
       assertInput(gSentenceRegExp.test(aDescription),
@@ -1166,17 +1144,17 @@ function getPCollsByProcess(aProcessMemo
       t._amount = aAmount;
       t._description = aDescription;
       if (aPresence !== undefined) {
         t._presence = aPresence;
       }
     }
   }
 
-  aProcessMemoryReports(ignoreSingle, ignoreMulti, handleReport);
+  aProcessReports(ignoreReporter, ignoreReport, handleReport);
 
   return pcollsByProcess;
 }
 
 //---------------------------------------------------------------------------
 
 // There are two kinds of TreeNode.
 // - Leaf TreeNodes correspond to reports.
@@ -2102,30 +2080,30 @@ function Compartment(aUnsafeName, aIsSys
 Compartment.prototype = {
   merge: function(aR) {
     this._nMerged = this._nMerged ? this._nMerged + 1 : 2;
   }
 };
 
 function getCompartmentsByProcess()
 {
-  // Ignore anything that didn't come from the "compartments" multi-reporter.
-  // (Note that some such reports can reach here as single reports if they were
-  // in the child process.)
+  // Ignore anything that didn't come from the "compartments" reporter.  (Note
+  // reports from this reporter can reach here via a "content-child" reporter
+  // if they were in a child process.)
 
-  function ignoreSingle(aUnsafePath)
+  function ignoreReporter(aName)
+  {
+    return !(aName == "compartments" || aName == "content-child");
+  }
+
+  function ignoreReport(aUnsafePath)
   {
     return !aUnsafePath.startsWith("compartments/");
   }
 
-  function ignoreMulti(aMRName)
-  {
-    return aMRName !== "compartments";
-  }
-
   let compartmentsByProcess = {};
 
   function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
                         aDescription)
   {
     let process = aProcess === "" ? gUnnamedProcessStr : aProcess;
     let unsafeNames = aUnsafePath.split('/');
     let isSystemCompartment;
@@ -2164,17 +2142,17 @@ function getCompartmentsByProcess()
       // Already an entry;  must be a duplicated compartment.  This can happen
       // legitimately.  Merge them.
       cOld.merge(c);
     } else {
       compartments[c._unsafeName] = c;
     }
   }
 
-  processMemoryReporters(ignoreSingle, ignoreMulti, handleReport);
+  processMemoryReporters(ignoreReporter, ignoreReport, handleReport);
 
   return compartmentsByProcess;
 }
 
 function GhostWindow(aUnsafeURL)
 {
   // Call it _unsafeName rather than _unsafeURL for symmetry with the
   // Compartment object.
@@ -2186,26 +2164,26 @@ function GhostWindow(aUnsafeURL)
 GhostWindow.prototype = {
   merge: function(aR) {
     this._nMerged = this._nMerged ? this._nMerged + 1 : 2;
   }
 };
 
 function getGhostWindowsByProcess()
 {
-  function ignoreSingle(aUnsafePath)
+  function ignoreReporter(aName)
+  {
+    return !(aName == "ghost-windows" || aName == "content-child");
+  }
+
+  function ignoreReport(aUnsafePath)
   {
     return !aUnsafePath.startsWith('ghost-windows/')
   }
 
-  function ignoreMulti(aName)
-  {
-    return aName !== "ghost-windows";
-  }
-
   let ghostWindowsByProcess = {};
 
   function handleReport(aProcess, aUnsafePath, aKind, aUnits, aAmount,
                         aDescription)
   {
     let unsafeSplit = aUnsafePath.split('/');
     assertInput(unsafeSplit[0] === 'ghost-windows' && unsafeSplit.length === 2,
            'Unexpected path in getGhostWindowsByProcess: ' + aUnsafePath);
@@ -2225,17 +2203,17 @@ function getGhostWindowsByProcess()
     if (ghostWindowsByProcess[process][unsafeURL]) {
       ghostWindowsByProcess[process][unsafeURL].merge(ghostWindow);
     }
     else {
       ghostWindowsByProcess[process][unsafeURL] = ghostWindow;
     }
   }
 
-  processMemoryReporters(ignoreSingle, ignoreMulti, handleReport);
+  processMemoryReporters(ignoreReporter, ignoreReport, handleReport);
 
   return ghostWindowsByProcess;
 }
 
 //---------------------------------------------------------------------------
 
 function appendProcessAboutCompartmentsElementsHelper(aP, aEntries, aKindString)
 {
--- a/toolkit/components/aboutmemory/tests/test_aboutcompartments.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutcompartments.xul
@@ -19,79 +19,69 @@
   <![CDATA[
   "use strict";
 
   const Cc = Components.classes;
   const Ci = Components.interfaces;
   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
 
-  // Remove all the real reporters and multi-reporters;  save them to
-  // restore at the end.
+  // Remove all the real reporters;  save them to restore at the end.
   mgr.blockRegistration();
   var e = mgr.enumerateReporters();
   var realReporters = [];
   while (e.hasMoreElements()) {
     var r = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
     mgr.unregisterReporter(r);
     realReporters.push(r);
   }
-  e = mgr.enumerateMultiReporters();
-  var realMultiReporters = [];
-  while (e.hasMoreElements()) {
-    var r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
-    mgr.unregisterMultiReporter(r);
-    realMultiReporters.push(r);
-  }
 
   // Setup various fake-but-deterministic reporters.
   const KB = 1024;
   const MB = KB * KB;
   const NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP;
   const HEAP    = Ci.nsIMemoryReporter.KIND_HEAP;
   const OTHER   = Ci.nsIMemoryReporter.KIND_OTHER;
 
   const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
   const COUNT = Ci.nsIMemoryReporter.UNITS_COUNT;
 
-  function f(aProcess, aPath, aKind, aUnits, aAmount) {
-    return {
-      process:     aProcess,
-      path:        aPath,
-      kind:        aKind,
-      units:       aUnits,
-      description: "",
-      amount:      aAmount 
-    };
-  }
-
   var fakeReporters = [
-    // These should be ignored.
-    f("", "explicit/a",         HEAP,    BYTES, 222 * MB),
-    f("", "explicit/b/a",       HEAP,    BYTES,  85 * MB),
-    f("", "explicit/b/b",       NONHEAP, BYTES,  85 * MB),
-    f("", "other1",             OTHER,   BYTES, 111 * MB),
-    f("", "other2",             OTHER,   COUNT, 888),
-
-    f("2nd", "explicit/c",      HEAP,    BYTES, 333 * MB),
-    f("2nd", "compartments/user/child-user-compartment",     OTHER, COUNT, 1),
-    f("2nd", "compartments/system/child-system-compartment", OTHER, COUNT, 1)
-  ];
-
-  var fakeMultiReporters = [
-    // These shouldn't show up.
-    { name: "fake",
+    { name: "fake1",
+      collectReports: function(aCbObj, aClosure) {
+        function f(aP1, aP2, aK, aU, aA) {
+          aCbObj.callback(aP1, aP2, aK, aU, aA, "Desc.", aClosure);
+        }
+        // These should be ignored.
+        f("", "explicit/a",         HEAP,    BYTES, 222 * MB);
+        f("", "explicit/b/a",       HEAP,    BYTES,  85 * MB);
+        f("", "explicit/b/b",       NONHEAP, BYTES,  85 * MB);
+        f("", "other1",             OTHER,   BYTES, 111 * MB);
+        f("", "other2",             OTHER,   COUNT, 888);
+      }
+    },
+    { name: "fake2",
       collectReports: function(aCbObj, aClosure) {
         function f(aP, aK, aU, aA) {
           aCbObj.callback("", aP, aK, aU, aA, "Desc.", aClosure);
         }
+        // These shouldn't show up.
         f("explicit/a/d",     HEAP,    BYTES,  13 * MB);
         f("explicit/b/c",     NONHEAP, BYTES,  10 * MB);
-       },
-       explicitNonHeap: 10*MB
+       }
+    },
+    { name: "content-child",
+      collectReports: function(aCbObj, aClosure) {
+        function f(aP1, aP2, aK, aU, aA, aD) {
+          aCbObj.callback(aP1, aP2, aK, aU, aA, aD, aClosure);
+        }
+        f("2nd", "explicit/c",      HEAP,    BYTES, 333 * MB, "Desc.");
+        f("2nd", "compartments/user/child-user-compartment",     OTHER, COUNT, 1, "");
+        f("2nd", "compartments/system/child-system-compartment", OTHER, COUNT, 1, "");
+      }
     },
     { name: "compartments",
       collectReports: function(aCbObj, aClosure) {
         function f(aP) {
           aCbObj.callback("", aP, OTHER, COUNT, 1, "", aClosure);
         }
         f("compartments/user/http:\\\\foo.com\\");
         f("compartments/user/https:\\\\bar.com\\bar?baz");
@@ -99,49 +89,43 @@
         // This moz-nullprincipal one is shown under "System Compartments" even
         // though its path indicates it's a user compartment.
         f("compartments/user/moz-nullprincipal:{7ddefdaf-34f1-473f-9b03-50a4568ccb06}");
         // This should show up once with a "[3]" suffix
         f("compartments/system/[System Principal]");
         f("compartments/system/[System Principal]");
         f("compartments/system/[System Principal]");
         f("compartments/system/atoms");
-      },
-      explicitNonHeap: 0
+      }
     },
     { name: "ghost-windows",
       collectReports: function(aCbObj, aClosure) {
         function f(aP) {
           aCbObj.callback("", aP, OTHER, COUNT, 1, "", aClosure);
         }
         f("ghost-windows/https:\\\\very-long-url.com\\very-long\\oh-so-long\\really-quite-long.html?a=2&b=3&c=4&d=5&e=abcdefghijklmnopqrstuvwxyz&f=123456789123456789123456789");
         f("ghost-windows/http:\\\\foobar.com\\foo?bar#baz");
-      },
-      explicitNonHeap: 0
+      }
     },
     // These shouldn't show up.
     { name: "smaps",
        collectReports: function(aCbObj, aClosure) {
         // The amounts are given in pages, so multiply here by 4kb.
         function f(aP, aA) {
           aCbObj.callback("", aP, NONHEAP, BYTES, aA * 4 * KB, "Desc.", aClosure);
         }
         f("smaps/vsize/a",     24);
         f("smaps/swap/a",       1);
-      },
-      explicitNonHeap: 0
+      }
     }
   ];
 
   for (var i = 0; i < fakeReporters.length; i++) {
     mgr.registerReporterEvenIfBlocked(fakeReporters[i]);
   }
-  for (var i = 0; i < fakeMultiReporters.length; i++) {
-    mgr.registerMultiReporterEvenIfBlocked(fakeMultiReporters[i]);
-  }
   ]]>
   </script>
 
   <!-- vary the capitalization to make sure that works -->
   <iframe id="acFrame"  height="400" src="abouT:compartMENTS"></iframe>
 
   <script type="application/javascript">
   <![CDATA[
@@ -175,31 +159,24 @@ System Compartments\n\
 child-system-compartment\n\
 \n\
 Ghost Windows\n\
 \n\
 ";
 
   function finish()
   {
-    // Unregister fake reporters and multi-reporters, re-register the real
-    // reporters and multi-reporters, just in case subsequent tests rely on
-    // them.
+    // Unregister fake reporters and re-register the real reporters, just in
+    // case subsequent tests rely on them.
     for (var i = 0; i < fakeReporters.length; i++) {
       mgr.unregisterReporter(fakeReporters[i]);
     }
-    for (var i = 0; i < fakeMultiReporters.length; i++) {
-      mgr.unregisterMultiReporter(fakeMultiReporters[i]);
-    }
     for (var i = 0; i < realReporters.length; i++) {
       mgr.registerReporterEvenIfBlocked(realReporters[i]);
     }
-    for (var i = 0; i < realMultiReporters.length; i++) {
-      mgr.registerMultiReporterEvenIfBlocked(realMultiReporters[i]);
-    }
     mgr.unblockRegistration();
 
     SimpleTest.finish();
   }
 
   // Cut+paste the entire page and check that the cut text matches what we
   // expect.  This tests the output in general and also that the cutting and
   // pasting works as expected.
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -22,157 +22,132 @@
   SimpleTest.expectAssertions(27);
 
   const Cc = Components.classes;
   const Ci = Components.interfaces;
   const Cr = Components.results;
   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
 
-  // Remove all the real reporters and multi-reporters;  save them to
-  // restore at the end.
+  // Remove all the real reporters;  save them to restore at the end.
   mgr.blockRegistration();
   let e = mgr.enumerateReporters();
   let realReporters = [];
   while (e.hasMoreElements()) {
     let r = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
     mgr.unregisterReporter(r);
     realReporters.push(r);
   }
-  e = mgr.enumerateMultiReporters();
-  let realMultiReporters = [];
-  while (e.hasMoreElements()) {
-    let r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
-    mgr.unregisterMultiReporter(r);
-    realMultiReporters.push(r);
-  }
 
   // Setup various fake-but-deterministic reporters.
   const KB = 1024;
   const MB = KB * KB;
   const NONHEAP = Ci.nsIMemoryReporter.KIND_NONHEAP;
   const HEAP    = Ci.nsIMemoryReporter.KIND_HEAP;
   const OTHER   = Ci.nsIMemoryReporter.KIND_OTHER;
 
   const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
   const COUNT = Ci.nsIMemoryReporter.UNITS_COUNT;
   const COUNT_CUMULATIVE = Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE;
   const PERCENTAGE = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
 
-  function f2(aProcess, aPath, aKind, aUnits, aAmount) {
-    return {
-      process:     aProcess,
-      path:        aPath,
-      kind:        aKind,
-      units:       aUnits,
-      description: "Desc.",
-      amount:      aAmount 
-    };
-  }
-
-  function f(aProcess, aPath, aKind, aAmount) {
-    return f2(aProcess, aPath, aKind, BYTES, aAmount);
-  }
-
   let fakeReporters = [
-    f("", "heap-allocated",     OTHER,   500 * MB),
-    f("", "heap-unallocated",   OTHER,   100 * MB),
-    f("", "explicit/a",         HEAP,    222 * MB),
-    f("", "explicit/b/a",       HEAP,     85 * MB),
-    f("", "explicit/b/b",       HEAP,     75 * MB),
-    f("", "explicit/b/c/a",     HEAP,     70 * MB),
-    f("", "explicit/b/c/b",     HEAP,      2 * MB), // omitted
-    f("", "explicit/g/a",       HEAP,      6 * MB),
-    f("", "explicit/g/b",       HEAP,      5 * MB),
-    f("", "explicit/g/other",   HEAP,      4 * MB),
-    // A degenerate tree with the same name as a non-degenerate tree should
-    // work ok.
-    f("", "explicit",           OTHER,   888 * MB),
-    f("", "other1/a/b",         OTHER,   111 * MB),
-    f("", "other1/c/d",         OTHER,    22 * MB),
-    f("", "other1/c/e",         OTHER,    33 * MB),
-    f2("", "other4",            OTHER,   COUNT_CUMULATIVE, 777),
-    f2("", "other4",            OTHER,   COUNT_CUMULATIVE, 111),
-    f2("", "other3/a/b/c/d/e",  OTHER,   PERCENTAGE, 2000),
-    f2("", "other3/a/b/c/d/f",  OTHER,   PERCENTAGE, 10),
-    f2("", "other3/a/b/c/d/g",  OTHER,   PERCENTAGE, 5),
-    f2("", "other3/a/b/c/d/g",  OTHER,   PERCENTAGE, 5),
-    // Check that a rounded-up-to-100.00% value is shown as "100.0%" (i.e. one
-    // decimal point).
-    f2("", "other6/big",        OTHER,   COUNT, 99999),
-    f2("", "other6/small",      OTHER,   COUNT, 1),
-    // Check that a 0 / 0 is handled correctly.
-    f("", "other7/zero",        OTHER,   0),
-    // These compartments ones shouldn't be displayed.
-    f2("", "compartments/user/foo",   OTHER, COUNT, 1),
-    f2("", "compartments/system/foo", OTHER, COUNT, 1)
-  ];
-  let fakeMultiReporters = [
+    { name: "fake0",
+      collectReports: function(aCbObj, aClosure) {
+        function f(aP, aK, aU, aA) {
+          aCbObj.callback("", aP, aK, aU, aA, "Desc.", aClosure);
+        }
+        f("heap-allocated",     OTHER,   BYTES, 500 * MB);
+        f("heap-unallocated",   OTHER,   BYTES, 100 * MB);
+        f("explicit/a",         HEAP,    BYTES, 222 * MB);
+        f("explicit/b/a",       HEAP,    BYTES,  85 * MB);
+        f("explicit/b/b",       HEAP,    BYTES,  75 * MB);
+        f("explicit/b/c/a",     HEAP,    BYTES,  70 * MB);
+        f("explicit/b/c/b",     HEAP,    BYTES,   2 * MB); // omitted
+        f("explicit/g/a",       HEAP,    BYTES,   6 * MB);
+        f("explicit/g/b",       HEAP,    BYTES,   5 * MB);
+        f("explicit/g/other",   HEAP,    BYTES,   4 * MB);
+        // A degenerate tree with the same name as a non-degenerate tree should
+        // work ok.
+        f("explicit",           OTHER,   BYTES, 888 * MB);
+        f("other1/a/b",         OTHER,   BYTES, 111 * MB);
+        f("other1/c/d",         OTHER,   BYTES,  22 * MB);
+        f("other1/c/e",         OTHER,   BYTES,  33 * MB);
+        f("other4",             OTHER,   COUNT_CUMULATIVE, 777);
+        f("other4",             OTHER,   COUNT_CUMULATIVE, 111);
+        f("other3/a/b/c/d/e",   OTHER,   PERCENTAGE, 2000);
+        f("other3/a/b/c/d/f",   OTHER,   PERCENTAGE, 10);
+        f("other3/a/b/c/d/g",   OTHER,   PERCENTAGE, 5);
+        f("other3/a/b/c/d/g",   OTHER,   PERCENTAGE, 5);
+        // Check that a rounded-up-to-100.00% value is shown as "100.0%" (i.e. one
+        // decimal point).
+        f("other6/big",         OTHER,   COUNT, 99999);
+        f("other6/small",       OTHER,   COUNT, 1);
+        // Check that a 0 / 0 is handled correctly.
+        f("other7/zero",        OTHER,   BYTES, 0);
+        // These compartments ones shouldn't be displayed.
+        f("compartments/user/foo",   OTHER, COUNT, 1);
+        f("compartments/system/foo", OTHER, COUNT, 1);
+      }
+    },
     { name: "fake1",
       collectReports: function(aCbObj, aClosure) {
         function f(aP, aK, aU, aA) {
           aCbObj.callback("", aP, aK, aU, aA, "Desc.", aClosure);
         }
-        f("explicit/c/d",     NONHEAP, BYTES,  13 * MB),
-        f("explicit/c/d",     NONHEAP, BYTES,  10 * MB), // dup
-        f("explicit/c/other", NONHEAP, BYTES,  77 * MB),
+        f("explicit/c/d",     NONHEAP, BYTES,  13 * MB);
+        f("explicit/c/d",     NONHEAP, BYTES,  10 * MB); // dup
+        f("explicit/c/other", NONHEAP, BYTES,  77 * MB);
         f("explicit/cc",      NONHEAP, BYTES,  13 * MB);
         f("explicit/cc",      NONHEAP, BYTES,  10 * MB); // dup
         f("explicit/d",       NONHEAP, BYTES, 499 * KB); // omitted
         f("explicit/e",       NONHEAP, BYTES, 100 * KB); // omitted
         f("explicit/f/g/h/i", HEAP,    BYTES,  10 * MB);
         f("explicit/f/g/h/j", HEAP,    BYTES,  10 * MB);
-      },
-      explicitNonHeap: (100 + 13 + 10)*MB + (499 + 100)*KB
+      }
     },
     { name: "fake2",
       collectReports: function(aCbObj, aClosure) {
         function f(aP, aK, aU, aA) {
           aCbObj.callback("", aP, aK, aU, aA, "Desc.", aClosure);
         }
         f("other3",           OTHER,   COUNT, 777);
         f("other2",           OTHER,   BYTES, 222 * MB);
         f("perc2",            OTHER,   PERCENTAGE, 10000);
         f("perc1",            OTHER,   PERCENTAGE, 4567);
-      },
-      explicitNonHeap: 0
+      }
     },
     { name: "smaps",
       collectReports: function(aCbObj, aClosure) {
         // The amounts are given in pages, so multiply here by 4kb.
         function f(aP, aA) {
           aCbObj.callback("", aP, NONHEAP, BYTES, aA * 4 * KB, "Desc.", aClosure);
         }
         f("size/a",      24);
         f("swap/a",       1);
         f("swap/a",       2);
         f("size/a",       19);
         f("swap/b/c",     10);
         f("rss/a",        42);
         f("pss/a",        43);
-      },
-      explicitNonHeap: 0
+      }
     },
     { name: "compartments",
       collectReports: function(aCbObj, aClosure) {
         function f(aP) {
           aCbObj.callback("", aP, OTHER, COUNT, 1, "", aClosure);
         }
         f("compartments/user/bar");
         f("compartments/system/bar");
-      },
-      explicitNonHeap: 0
+      }
     }
   ];
   for (let i = 0; i < fakeReporters.length; i++) {
     mgr.registerReporterEvenIfBlocked(fakeReporters[i]);
   }
-  for (let i = 0; i < fakeMultiReporters.length; i++) {
-    mgr.registerMultiReporterEvenIfBlocked(fakeMultiReporters[i]);
-  }
 
   // mgr.explicit sums "heap-allocated" and all the appropriate NONHEAP ones:
   // - "explicit/c", "explicit/cc" x 2, "explicit/d", "explicit/e"
   // - but *not* "explicit/c/d" x 2
   // Check explicit now before we add the fake reporters for the fake 2nd
   // and subsequent processes.
   //
   // Nb: mgr.explicit will throw NS_ERROR_NOT_AVAILABLE if this is a
@@ -184,69 +159,84 @@
     is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception");
   }
  
   // The main process always comes first when we display about:memory.  The
   // remaining processes are sorted by their |resident| values (starting with
   // the largest).  Processes without a |resident| memory reporter are saved
   // for the end.
   let fakeReporters2 = [
-    f("2nd", "heap-allocated",  OTHER,  1000 * MB),
-    f("2nd", "heap-unallocated",OTHER,   100 * MB),
-    f("2nd", "explicit/a/b/c",  HEAP,    497 * MB),
-    f("2nd", "explicit/a/b/c",  HEAP,      1 * MB), // dup: merge
-    f("2nd", "explicit/a/b/c",  HEAP,      1 * MB), // dup: merge
-    f("2nd", "explicit/flip\\the\\backslashes",
-                                HEAP,    200 * MB),
-    f("2nd", "explicit/compartment(compartment-url)",
-                                HEAP,    200 * MB),
-    f("2nd", "other0",          OTHER,   666 * MB),
-    f("2nd", "other1",          OTHER,   111 * MB),
-    // Even though the "smaps" reporter is a multi-reporter, if its in a
-    // child process it'll be passed to the main process as single reports.
-    // The fact that we skip the "smaps" multi-reporter in the main
-    // process won't cause these to be skipped;  the fall-back skipping will
-    // be hit instead.
-    f("2nd", "size/e",          NONHEAP, 24*4*KB),
-    f("2nd", "size/f",          NONHEAP, 24*4*KB),
-
-    // Check that we can handle "heap-allocated" not being present.
-    f("3rd", "explicit/a/b",    HEAP,    333 * MB),
-    f("3rd", "explicit/a/c",    HEAP,    444 * MB),
-    f("3rd", "other1",          OTHER,     1 * MB),
-    f("3rd", "resident",        OTHER,   100 * MB),
+    { name: "resident-fast",
+      collectReports: function(aCbObj, aClosure) {
+        // Shouldn't reach here;  aboutMemory.js should skip the reporter.
+        // (Nb: this must come after |mgr.explicit| is accessed, otherwise it
+        // *will* be run by nsMemoryReporterManager::GetExplicit()).
+        ok(false, "'resident-fast' reporter was run");
+      }
+    },
+    { name: "fake3",
+      collectReports: function(aCbObj, aClosure) {
+        function f(aP1, aP2, aK, aU, aA) {
+          aCbObj.callback(aP1, aP2, aK, aU, aA, "Desc.", aClosure);
+        }
+        f("2nd", "heap-allocated",  OTHER,   BYTES,1000* MB);
+        f("2nd", "heap-unallocated",OTHER,   BYTES,100 * MB);
+        f("2nd", "explicit/a/b/c",  HEAP,    BYTES,497 * MB);
+        f("2nd", "explicit/a/b/c",  HEAP,    BYTES,  1 * MB); // dup: merge
+        f("2nd", "explicit/a/b/c",  HEAP,    BYTES,  1 * MB); // dup: merge
+        f("2nd", "explicit/flip\\the\\backslashes",
+                                    HEAP,    BYTES,200 * MB);
+        f("2nd", "explicit/compartment(compartment-url)",
+                                    HEAP,    BYTES,200 * MB);
+        f("2nd", "other0",          OTHER,   BYTES,666 * MB);
+        f("2nd", "other1",          OTHER,   BYTES,111 * MB);
+        // If the "smaps" reporter is in a child process it'll be passed to
+        // the main process under a different name.  The fact that we skip
+        // the "smaps" reporter in the main process won't cause these
+        // to be skipped;  the report-level skipping will be hit instead.
+        f("2nd", "size/e",          NONHEAP, BYTES,24*4*KB);
+        f("2nd", "size/f",          NONHEAP, BYTES,24*4*KB);
+        f("2nd", "resident-fast",   NONHEAP, BYTES,24*4*KB); // ignored!
 
-    // Invalid values (negative, too-big) should be identified.
-    f("4th", "heap-allocated",   OTHER,   100 * MB),
-    f("4th", "resident",         OTHER,   200 * MB),
-    f("4th", "explicit/js/compartment(http:\\\\too-big.com\\)/stuff",
-                                 HEAP,    150 * MB),
-    f("4th", "explicit/ok",      HEAP,      5 * MB),
-    f("4th", "explicit/neg1",    NONHEAP,  -2 * MB),
-    // -111 becomes "-0.00MB" in non-verbose mode, and getting the negative
-    // sign in there correctly is non-trivial.
-    f("4th",  "other1",          OTHER,   -111),
-    f("4th",  "other2",          OTHER,   -222 * MB),
-    f2("4th", "other3",          OTHER,   COUNT, -333),
-    f2("4th", "other4",          OTHER,   COUNT_CUMULATIVE, -444),
-    f2("4th", "other5",          OTHER,   PERCENTAGE, -555),
-    f2("4th", "other6",          OTHER,   PERCENTAGE, 66666),
+        // Check that we can handle "heap-allocated" not being present.
+        f("3rd", "explicit/a/b",    HEAP,    BYTES,333 * MB);
+        f("3rd", "explicit/a/c",    HEAP,    BYTES,444 * MB);
+        f("3rd", "other1",          OTHER,   BYTES,  1 * MB);
+        f("3rd", "resident",        OTHER,   BYTES,100 * MB);
 
-    // If a negative value is within a collapsed sub-tree in non-verbose mode,
-    // we should get the warning at the top and the relevant sub-trees should
-    // be expanded, even in non-verbose mode.
-    f("5th", "heap-allocated",   OTHER,   100 * MB),
-    f("5th", "explicit/big",     HEAP,     99 * MB),
-    f("5th", "explicit/a/pos",   HEAP,     40 * KB),
-    f("5th", "explicit/a/neg1",  NONHEAP, -20 * KB),
-    f("5th", "explicit/a/neg2",  NONHEAP, -10 * KB),
-    f("5th", "explicit/b/c/d/e", NONHEAP,  20 * KB),
-    f("5th", "explicit/b/c/d/f", NONHEAP, -60 * KB),
-    f("5th", "explicit/b/c/g/h", NONHEAP,  10 * KB),
-    f("5th", "explicit/b/c/i/j", NONHEAP,   5 * KB)
+        // Invalid values (negative, too-big) should be identified.
+        f("4th", "heap-allocated",   OTHER,   BYTES,100 * MB);
+        f("4th", "resident",         OTHER,   BYTES,200 * MB);
+        f("4th", "explicit/js/compartment(http:\\\\too-big.com\\)/stuff",
+                                     HEAP,    BYTES,150 * MB);
+        f("4th", "explicit/ok",      HEAP,    BYTES,  5 * MB);
+        f("4th", "explicit/neg1",    NONHEAP, BYTES, -2 * MB);
+        // -111 becomes "-0.00MB" in non-verbose mode, and getting the negative
+        // sign in there correctly is non-trivial.
+        f("4th",  "other1",          OTHER,   BYTES,-111);
+        f("4th",  "other2",          OTHER,   BYTES,-222 * MB);
+        f("4th",  "other3",          OTHER,   COUNT, -333);
+        f("4th",  "other4",          OTHER,   COUNT_CUMULATIVE, -444);
+        f("4th",  "other5",          OTHER,   PERCENTAGE, -555);
+        f("4th",  "other6",          OTHER,   PERCENTAGE, 66666);
+
+        // If a negative value is within a collapsed sub-tree in non-verbose mode,
+        // we should get the warning at the top and the relevant sub-trees should
+        // be expanded, even in non-verbose mode.
+        f("5th", "heap-allocated",   OTHER,   BYTES,100 * MB);
+        f("5th", "explicit/big",     HEAP,    BYTES, 99 * MB);
+        f("5th", "explicit/a/pos",   HEAP,    BYTES, 40 * KB);
+        f("5th", "explicit/a/neg1",  NONHEAP, BYTES,-20 * KB);
+        f("5th", "explicit/a/neg2",  NONHEAP, BYTES,-10 * KB);
+        f("5th", "explicit/b/c/d/e", NONHEAP, BYTES, 20 * KB);
+        f("5th", "explicit/b/c/d/f", NONHEAP, BYTES,-60 * KB);
+        f("5th", "explicit/b/c/g/h", NONHEAP, BYTES, 10 * KB);
+        f("5th", "explicit/b/c/i/j", NONHEAP, BYTES,  5 * KB);
+      }
+    }
   ];
   for (let i = 0; i < fakeReporters2.length; i++) {
     mgr.registerReporterEvenIfBlocked(fakeReporters2[i]);
   }
   fakeReporters = fakeReporters.concat(fakeReporters2);
   ]]>
   </script>
 
@@ -581,31 +571,24 @@ 104,801,280 B (100.0%) -- explicit\n\
 Other Measurements\n\
 \n\
 104,857,600 B ── heap-allocated\n\
 \n\
 "
 
   function finish()
   {
-    // Unregister fake reporters and multi-reporters, re-register the real
-    // reporters and multi-reporters, just in case subsequent tests rely on
-    // them.
+    // Unregister fake reporters and re-register the real reporters, just in
+    // case subsequent tests rely on them.
     for (let i = 0; i < fakeReporters.length; i++) {
       mgr.unregisterReporter(fakeReporters[i]);
     }
-    for (let i = 0; i < fakeMultiReporters.length; i++) {
-      mgr.unregisterMultiReporter(fakeMultiReporters[i]);
-    }
     for (let i = 0; i < realReporters.length; i++) {
       mgr.registerReporterEvenIfBlocked(realReporters[i]);
     }
-    for (let i = 0; i < realMultiReporters.length; i++) {
-      mgr.registerMultiReporterEvenIfBlocked(realMultiReporters[i]);
-    }
     mgr.unblockRegistration();
 
     SimpleTest.finish();
   }
 
   // Cut+paste the entire page and check that the cut text matches what we
   // expect.  This tests the output in general and also that the cutting and
   // pasting works as expected.
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
@@ -17,120 +17,104 @@
   <![CDATA[
   "use strict";
 
   const Cc = Components.classes;
   const Ci = Components.interfaces;
   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
 
-  // Remove all the real reporters and multi-reporters;  save them to
-  // restore at the end.
+  // Remove all the real reporters;  save them to restore at the end.
   mgr.blockRegistration();
   let e = mgr.enumerateReporters();
   let realReporters = [];
   while (e.hasMoreElements()) {
     let r = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
     mgr.unregisterReporter(r);
     realReporters.push(r);
   }
-  e = mgr.enumerateMultiReporters();
-  let realMultiReporters = [];
-  while (e.hasMoreElements()) {
-    let r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
-    mgr.unregisterMultiReporter(r);
-    realMultiReporters.push(r);
-  }
 
   // Setup various fake-but-deterministic reporters.
   const KB = 1024;
   const MB = KB * KB;
   const HEAP  = Ci.nsIMemoryReporter.KIND_HEAP;
   const OTHER = Ci.nsIMemoryReporter.KIND_OTHER;
   const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
 
-  function f(aPath, aKind, aAmount) {
-    return {
-      process:     "",
-      path:        aPath,
-      kind:        aKind,
-      units:       BYTES,
-      description: "Desc.",
-      amount:      aAmount
-    };
-  }
-
-  let hiReport  = f("explicit/h/i",  HEAP,  10 * MB);
-  let hi2Report = f("explicit/h/i2", HEAP,   9 * MB);
-  let jkReport  = f("explicit/j/k",  HEAP, 0.5 * MB);
-  let jk2Report = f("explicit/j/k2", HEAP, 0.3 * MB);
+  let hiPath  = "explicit/h/i";
+  let hi2Path = "explicit/h/i2";
+  let jkPath  = "explicit/j/k";
+  let jk2Path = "explicit/j/k2";
 
   let fakeReporters = [
-    f("heap-allocated",     OTHER,   250 * MB),
-    f("explicit/a/b",       HEAP,     50 * MB),
-    f("explicit/a/c/d",     HEAP,     25 * MB),
-    f("explicit/a/c/e",     HEAP,     15 * MB),
-    f("explicit/a/f",       HEAP,     30 * MB),
-    f("explicit/g",         HEAP,    100 * MB),
-    hiReport,
-    hi2Report,
-    jkReport,
-    jk2Report,
-    f("explicit/a/l/m",     HEAP,    0.1 * MB),
-    f("explicit/a/l/n",     HEAP,    0.1 * MB),
+    { name: "fake1",
+      collectReports: function(aCbObj, aClosure) {
+        function f(aP, aK, aA) {
+          aCbObj.callback("", aP, aK, BYTES, aA, "Desc.", aClosure);
+        }
+        f("heap-allocated",     OTHER,   250 * MB);
+        f("explicit/a/b",       HEAP,     50 * MB);
+        f("explicit/a/c/d",     HEAP,     25 * MB);
+        f("explicit/a/c/e",     HEAP,     15 * MB);
+        f("explicit/a/f",       HEAP,     30 * MB);
+        f("explicit/g",         HEAP,    100 * MB);
+        f(hiPath,               HEAP,     10 * MB);
+        f(hi2Path,              HEAP,      9 * MB);
+        f(jkPath,               HEAP,    0.5 * MB);
+        f(jk2Path,              HEAP,    0.3 * MB);
+        f("explicit/a/l/m",     HEAP,    0.1 * MB);
+        f("explicit/a/l/n",     HEAP,    0.1 * MB);
+      }
+    }
   ];
 
   for (let i = 0; i < fakeReporters.length; i++) {
     mgr.registerReporterEvenIfBlocked(fakeReporters[i]);
   }
 
   ]]>
   </script>
 
   <iframe id="amFrame"  height="500" src="about:memory"></iframe>
 
   <script type="application/javascript">
   <![CDATA[
   function finish()
   {
-    // Unregister fake reporters and multi-reporters, re-register the real
-    // reporters and multi-reporters, just in case subsequent tests rely on
-    // them.
+    // Unregister fake reporters and re-register the real reporters, just in
+    // case subsequent tests rely on them.
     for (let i = 0; i < fakeReporters.length; i++) {
       mgr.unregisterReporter(fakeReporters[i]);
     }
     for (let i = 0; i < realReporters.length; i++) {
       mgr.registerReporterEvenIfBlocked(realReporters[i]);
     }
-    for (let i = 0; i < realMultiReporters.length; i++) {
-      mgr.registerMultiReporterEvenIfBlocked(realMultiReporters[i]);
-    }
     mgr.unblockRegistration();
 
     SimpleTest.finish();
   }
 
   // Click on the identified element, then cut+paste the entire page and
   // check that the cut text matches what we expect.
   function test(aId, aSwap, aExpected, aNext) {
     let win = document.getElementById("amFrame").contentWindow;
     if (aId) {
       let node = win.document.getElementById(aId);
 
       // Yuk:  clicking a button is easy;  but for tree entries we need to
       // click on a child of the span identified via |id|.
       if (node.nodeName === "button") {
         if (aSwap) {
-          // We swap the paths of hiReport/hi2Report and jkReport/jk2Report
-          // just before updating, to test what happens when significant nodes
-          // become insignificant and vice versa.
-          hiReport.path  = "explicit/j/k";
-          hi2Report.path = "explicit/j/k2";
-          jkReport.path  = "explicit/h/i";
-          jk2Report.path = "explicit/h/i2";
+          // We swap hipath/hi2Path and jkPath/jk2Path just before updating, to
+          // test what happens when significant nodes become insignificant and
+          // vice versa.
+          hiPath  = "explicit/j/k";
+          hi2Path = "explicit/j/k2";
+          jkPath  = "explicit/h/i";
+          jk2Path = "explicit/h/i2";
         }
         node.click();
       } else {
         node.childNodes[0].click();
       }
     }
 
     SimpleTest.executeSoon(function() {
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
@@ -17,84 +17,68 @@
   <![CDATA[
   "use strict";
 
   const Cc = Components.classes;
   const Ci = Components.interfaces;
   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
 
-  // Remove all the real reporters and multi-reporters;  save them to
-  // restore at the end.
+  // Remove all the real reporters;  save them to restore at the end.
   mgr.blockRegistration();
   let e = mgr.enumerateReporters();
   let realReporters = [];
   while (e.hasMoreElements()) {
     let r = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
     mgr.unregisterReporter(r);
     realReporters.push(r);
   }
-  e = mgr.enumerateMultiReporters();
-  let realMultiReporters = [];
-  while (e.hasMoreElements()) {
-    let r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
-    mgr.unregisterMultiReporter(r);
-    realMultiReporters.push(r);
-  }
 
   // Setup a minimal number of fake reporters.
   const KB = 1024;
   const MB = KB * KB;
   const HEAP  = Ci.nsIMemoryReporter.KIND_HEAP;
   const OTHER = Ci.nsIMemoryReporter.KIND_OTHER;
   const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
 
-  function f(aPath, aKind, aAmount, aDesc) {
-    return {
-      process:     "",
-      path:        aPath,
-      kind:        aKind,
-      units:       BYTES,
-      amount:      aAmount,
-      description: aDesc
-    };
-  }
-
   let fakeReporters = [
-    f("heap-allocated",     OTHER,   250 * MB, "Heap allocated."),
-    f("explicit/a/b",       HEAP,     50 * MB, "A b."),
-    f("other/a",            OTHER,   0.2 * MB, "Other a."),
-    f("other/b",            OTHER,   0.1 * MB, "Other b."),
+    { name: "fake1",
+      collectReports: function(aCbObj, aClosure) {
+        function f(aP, aK, aA, aD) {
+          aCbObj.callback("", aP, aK, BYTES, aA, aD, aClosure);
+        }
+        f("heap-allocated",     OTHER,   250 * MB, "Heap allocated.");
+        f("explicit/a/b",       HEAP,     50 * MB, "A b.");
+        f("other/a",            OTHER,   0.2 * MB, "Other a.");
+        f("other/b",            OTHER,   0.1 * MB, "Other b.");
+      }
+    }
   ];
 
   for (let i = 0; i < fakeReporters.length; i++) {
     mgr.registerReporterEvenIfBlocked(fakeReporters[i]);
   }
 
   ]]>
   </script>
 
   <iframe id="amFrame"  height="400" src="about:memory"></iframe>
 
   <script type="application/javascript">
   <![CDATA[
   function finish()
   {
-    // Unregister fake reporters and multi-reporters, re-register the real
-    // reporters and multi-reporters, just in case subsequent tests rely on
-    // them.
+    // Unregister fake reporters and re-register the real reporters, just in
+    // case subsequent tests rely on them.
     for (let i = 0; i < fakeReporters.length; i++) {
       mgr.unregisterReporter(fakeReporters[i]);
     }
     for (let i = 0; i < realReporters.length; i++) {
       mgr.registerReporterEvenIfBlocked(realReporters[i]);
     }
-    for (let i = 0; i < realMultiReporters.length; i++) {
-      mgr.registerMultiReporterEvenIfBlocked(realMultiReporters[i]);
-    }
     mgr.unblockRegistration();
 
     SimpleTest.finish();
   }
 
   // Load the given file into the frame, then copy+paste the entire frame and
   // check that the cut text matches what we expect.
   function test(aFilename, aFilename2, aExpected, aDumpFirst, aNext) {
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
@@ -133,21 +133,16 @@
     is(ex.result, Cr.NS_ERROR_NOT_AVAILABLE, "mgr.explicit exception");
     haveExplicit = false;
   }
   dummy = mgr.resident;
 
   let e = mgr.enumerateReporters();
   while (e.hasMoreElements()) {
     let r = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
-    handleReport(r.process, r.path, r.kind, r.units, r.amount, r.description);
-  }
-  e = mgr.enumerateMultiReporters();
-  while (e.hasMoreElements()) {
-    let r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
     r.collectReports(handleReport, null);
 
     // Access |name| to make sure it doesn't crash or assert.
     dummy = r.name;
   }
 
   function checkSpecialReport(aName, aAmounts)
   {
--- a/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul
+++ b/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul
@@ -31,23 +31,23 @@
   file.append("test_sqliteMultiReporter-fake-DB-tmp.sqlite");
 
   // Open and close the DB.
   let storage = Cc["@mozilla.org/storage/service;1"].
                 getService(Ci.mozIStorageService);
   let db = storage.openDatabase(file);
   db.close();
 
-  // Invoke all the multi-reporters.  The SQLite multi-reporter is among
+  // Invoke all the reporters.  The SQLite multi-reporter is among
   // them.  It shouldn't crash.
   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
-  e = mgr.enumerateMultiReporters();
+  e = mgr.enumerateReporters();
   while (e.hasMoreElements()) {
-    let r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter);
+    let r = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
     r.collectReports(function(){}, null);
   }
 
   // If we haven't crashed, we've passed, but the test harness requires that
   // we explicitly check something.
   ok(true, "didn't crash");
 
   ]]>
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -1903,21 +1903,21 @@ StoreAndNotifyEmbedVisit(VisitData& aPla
     (void)NS_ProxyRelease(mainThread, aCallback, true);
   }
 
   VisitData noReferrer;
   nsCOMPtr<nsIRunnable> event = new NotifyVisitObservers(aPlace, noReferrer);
   (void)NS_DispatchToMainThread(event);
 }
 
-class HistoryLinksHashtableReporter MOZ_FINAL : public MemoryReporterBase
+class HistoryLinksHashtableReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   HistoryLinksHashtableReporter()
-    : MemoryReporterBase("explicit/history-links-hashtable",
+    : MemoryUniReporter("explicit/history-links-hashtable",
                          KIND_HEAP, UNITS_BYTES,
 "Memory used by the hashtable that records changes to the visited state of "
 "links.")
     {}
 private:
   int64_t Amount() MOZ_OVERRIDE
   {
     History* history = History::GetService();
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -358,21 +358,21 @@ TelemetryImpl::SizeOfIncludingThis(mozil
 
   for (HistogramIterator it = hs.begin(); it != hs.end(); ++it) {
     Histogram *h = *it;
     n += h->SizeOfIncludingThis(aMallocSizeOf);
   }
   return n;
 }
 
-class TelemetryReporter MOZ_FINAL : public MemoryReporterBase
+class TelemetryReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   TelemetryReporter()
-    : MemoryReporterBase("explicit/telemetry", KIND_HEAP, UNITS_BYTES,
+    : MemoryUniReporter("explicit/telemetry", KIND_HEAP, UNITS_BYTES,
                          "Memory used by the telemetry system.")
   {}
 private:
   int64_t Amount() MOZ_OVERRIDE
   {
     return TelemetryImpl::SizeOfIncludingThis(MallocSizeOf);
   }
 };
--- a/toolkit/components/telemetry/TelemetryPing.js
+++ b/toolkit/components/telemetry/TelemetryPing.js
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 const Cu = Components.utils;
 
+Cu.import("resource://gre/modules/debug.js");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 #ifndef MOZ_WIDGET_GONK
 Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
 #endif
 Cu.import("resource://gre/modules/ctypes.jsm");
 Cu.import("resource://gre/modules/ThirdPartyCookieProbe.jsm");
 Cu.import("resource://gre/modules/TelemetryFile.jsm");
@@ -453,37 +454,50 @@ TelemetryPing.prototype = {
       return;
     }
 
     let histogram = Telemetry.getHistogramById("TELEMETRY_MEMORY_REPORTER_MS");
     let startTime = new Date();
     let e = mgr.enumerateReporters();
     while (e.hasMoreElements()) {
       let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
-      let id = MEM_HISTOGRAMS[mr.path];
+      let id = MEM_HISTOGRAMS[mr.name];
       if (!id) {
         continue;
       }
 
-      // Reading mr.amount might throw an exception.  If so, just ignore that
+      // collectReports might throw an exception.  If so, just ignore that
       // memory reporter; we're not getting useful data out of it.
       try {
-        this.handleMemoryReport(id, mr.path, mr.units, mr.amount);
+        // Bind handleMemoryReport() so it can be called inside the closure
+        // used as the callback.
+        let boundHandleMemoryReport = this.handleMemoryReport.bind(this);
+
+        // Reporters used for telemetry should be uni-reporters!  we assert if
+        // they make more than one report.
+        let hasReported = false;
+
+        function h(process, path, kind, units, amount, desc) {
+          if (!hasReported) {
+            boundHandleMemoryReport(id, path, units, amount);
+            hasReported = true;
+          } else {
+            NS_ASSERT(false,
+                      "reporter " + mr.name + " has made more than one report");
+          }
+        }
+        mr.collectReports(h, null);
       }
       catch (e) {
       }
     }
     histogram.add(new Date() - startTime);
   },
 
-  handleMemoryReport: function handleMemoryReport(id, path, units, amount) {
-    if (amount == -1) {
-      return;
-    }
-
+  handleMemoryReport: function(id, path, units, amount) {
     let val;
     if (units == Ci.nsIMemoryReporter.UNITS_BYTES) {
       val = Math.floor(amount / 1024);
     }
     else if (units == Ci.nsIMemoryReporter.UNITS_PERCENTAGE) {
       // UNITS_PERCENTAGE amounts are 100x greater than their raw value.
       val = Math.floor(amount / 100);
     }
--- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
@@ -31,22 +31,22 @@ using namespace mozilla;
 static const PRLogModuleInfo *gUrlClassifierPrefixSetLog = nullptr;
 #define LOG(args) PR_LOG(gUrlClassifierPrefixSetLog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierPrefixSetLog, 4)
 #else
 #define LOG(args)
 #define LOG_ENABLED() (false)
 #endif
 
-class PrefixSetReporter MOZ_FINAL : public MemoryReporterBase
+class PrefixSetReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   PrefixSetReporter(nsUrlClassifierPrefixSet* aPrefixSet,
                     const nsACString& aName)
-    : MemoryReporterBase(
+    : MemoryUniReporter(
         nsPrintfCString(
           "explicit/storage/prefix-set/%s",
           (!aName.IsEmpty() ? PromiseFlatCString(aName).get() : "?!")
         ).get(),
         KIND_HEAP, UNITS_BYTES,
         "Memory used by the prefix set for a URL classifier.")
     , mPrefixSet(aPrefixSet)
   {}
--- a/xpcom/base/AvailableMemoryTracker.cpp
+++ b/xpcom/base/AvailableMemoryTracker.cpp
@@ -320,125 +320,70 @@ CreateDIBSectionHook(HDC aDC,
 
   if (doCheck) {
     CheckMemAvailable();
   }
 
   return result;
 }
 
-class LowMemoryEventsVirtualReporter MOZ_FINAL : public MemoryReporterBase
+class LowMemoryEventsVirtualReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
-  // The description is "???" because we implement GetDescription().
   LowMemoryEventsVirtualReporter()
-    : MemoryReporterBase("low-memory-events/virtual",
-                         KIND_OTHER, UNITS_COUNT_CUMULATIVE, "???")
+    : MemoryUniReporter("low-memory-events/virtual",
+                         KIND_OTHER, UNITS_COUNT_CUMULATIVE,
+"Number of low-virtual-memory events fired since startup. We fire such an "
+"event if we notice there is less than memory.low_virtual_mem_threshold_mb of "
+"virtual address space available (if zero, this behavior is disabled).  The "
+"process will probably crash if it runs out of virtual address space, so "
+"this event is dire.")
   {}
 
-  NS_IMETHOD GetDescription(nsACString &aDescription)
-  {
-    aDescription.AssignLiteral(
-      "Number of low-virtual-memory events fired since startup. ");
-
-    if (sLowVirtualMemoryThreshold == 0) {
-      aDescription.AppendLiteral(
-        "Tracking low-virtual-memory events is disabled, but you can enable it "
-        "by giving the memory.low_virtual_mem_threshold_mb pref a non-zero "
-        "value.");
-    }
-    else {
-      aDescription.Append(nsPrintfCString(
-        "We fire such an event if we notice there is less than %d MB of virtual "
-        "address space available (controlled by the "
-        "'memory.low_virtual_mem_threshold_mb' pref).  We'll likely crash if "
-        "we run out of virtual address space, so this event is somewhat dire.",
-        sLowVirtualMemoryThreshold));
-    }
-    return NS_OK;
-  }
-
 private:
   int64_t Amount() MOZ_OVERRIDE
   {
     // This memory reporter shouldn't be installed on 64-bit machines, since we
     // force-disable virtual-memory tracking there.
     MOZ_ASSERT(sizeof(void*) == 4);
 
     return sNumLowVirtualMemEvents;
   }
 };
 
-class LowCommitSpaceEventsReporter MOZ_FINAL : public MemoryReporterBase
+class LowCommitSpaceEventsReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
-  // The description is "???" because we implement GetDescription().
   LowCommitSpaceEventsReporter()
-    : MemoryReporterBase("low-commit-space-events",
-                         KIND_OTHER, UNITS_COUNT_CUMULATIVE, "???")
+    : MemoryUniReporter("low-commit-space-events",
+                         KIND_OTHER, UNITS_COUNT_CUMULATIVE,
+"Number of low-commit-space events fired since startup. We fire such an "
+"event if we notice there is less than memory.low_commit_space_threshold_mb of "
+"commit space available (if zero, this behavior is disabled).  Windows will "
+"likely kill the process if it runs out of commit space, so this event is "
+"dire.")
   {}
 
-  NS_IMETHOD GetDescription(nsACString &aDescription)
-  {
-    aDescription.AssignLiteral(
-      "Number of low-commit-space events fired since startup. ");
-
-    if (sLowCommitSpaceThreshold == 0) {
-      aDescription.Append(
-        "Tracking low-commit-space events is disabled, but you can enable it "
-        "by giving the memory.low_commit_space_threshold_mb pref a non-zero "
-        "value.");
-    }
-    else {
-      aDescription.Append(nsPrintfCString(
-        "We fire such an event if we notice there is less than %d MB of "
-        "available commit space (controlled by the "
-        "'memory.low_commit_space_threshold_mb' pref).  Windows will likely "
-        "kill us if we run out of commit space, so this event is somewhat dire.",
-        sLowCommitSpaceThreshold));
-    }
-    return NS_OK;
-  }
-
 private:
   int64_t Amount() MOZ_OVERRIDE { return sNumLowCommitSpaceEvents; }
 };
 
-class LowMemoryEventsPhysicalReporter MOZ_FINAL : public MemoryReporterBase
+class LowMemoryEventsPhysicalReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
-  // The description is "???" because we implement GetDescription().
   LowMemoryEventsPhysicalReporter()
-    : MemoryReporterBase("low-memory-events/physical",
-                         KIND_OTHER, UNITS_COUNT_CUMULATIVE, "???")
+    : MemoryUniReporter("low-memory-events/physical",
+                         KIND_OTHER, UNITS_COUNT_CUMULATIVE,
+"Number of low-physical-memory events fired since startup. We fire such an "
+"event if we notice there is less than memory.low_physical_memory_threshold_mb "
+"of physical memory available (if zero, this behavior is disabled).  The "
+"machine will start to page if it runs out of physical memory.  This may "
+"cause it to run slowly, but it shouldn't cause it to crash.")
   {}
 
-  NS_IMETHOD GetDescription(nsACString &aDescription)
-  {
-    aDescription.AssignLiteral(
-      "Number of low-physical-memory events fired since startup. ");
-
-    if (sLowPhysicalMemoryThreshold == 0) {
-      aDescription.Append(
-        "Tracking low-physical-memory events is disabled, but you can enable it "
-        "by giving the memory.low_physical_memory_threshold_mb pref a non-zero "
-        "value.");
-    }
-    else {
-      aDescription.Append(nsPrintfCString(
-        "We fire such an event if we notice there is less than %d MB of "
-        "available physical memory (controlled by the "
-        "'memory.low_physical_memory_threshold_mb' pref).  The machine will start "
-        "to page if it runs out of physical memory; this may cause it to run "
-        "slowly, but it shouldn't cause us to crash.",
-        sLowPhysicalMemoryThreshold));
-    }
-    return NS_OK;
-  }
-
 private:
   int64_t Amount() MOZ_OVERRIDE { return sNumLowPhysicalMemEvents; }
 };
 
 #endif // defined(XP_WIN)
 
 /**
  * This runnable is executed in response to a memory-pressure event; we spin
--- a/xpcom/base/MapsMemoryReporter.cpp
+++ b/xpcom/base/MapsMemoryReporter.cpp
@@ -106,79 +106,79 @@ struct CategoriesSeen {
   bool mSeenRss;
   bool mSeenPss;
   bool mSeenSize;
   bool mSeenSwap;
 };
 
 } // anonymous namespace
 
-class MapsReporter MOZ_FINAL : public nsIMemoryMultiReporter
+class MapsReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   MapsReporter();
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
   NS_IMETHOD GetName(nsACString &aName)
   {
       aName.AssignLiteral("smaps");
       return NS_OK;
   }
 
   NS_IMETHOD
-  CollectReports(nsIMemoryMultiReporterCallback *aCb,
+  CollectReports(nsIMemoryReporterCallback *aCb,
                  nsISupports *aClosure);
 
 private:
   // Search through /proc/self/maps for libxul.so, and set mLibxulDir to the
   // the directory containing libxul.
   nsresult FindLibxul();
 
   nsresult
   ParseMapping(FILE *aFile,
-               nsIMemoryMultiReporterCallback *aCb,
+               nsIMemoryReporterCallback *aCb,
                nsISupports *aClosure,
                CategoriesSeen *aCategoriesSeen);
 
   void
   GetReporterNameAndDescription(const char *aPath,
                                 const char *aPermissions,
                                 nsACString &aName,
                                 nsACString &aDesc);
 
   nsresult
   ParseMapBody(FILE *aFile,
                const nsACString &aName,
                const nsACString &aDescription,
-               nsIMemoryMultiReporterCallback *aCb,
+               nsIMemoryReporterCallback *aCb,
                nsISupports *aClosure,
                CategoriesSeen *aCategoriesSeen);
 
   bool mSearchedForLibxul;
   nsCString mLibxulDir;
   nsTHashtable<nsCStringHashKey> mMozillaLibraries;
 };
 
-NS_IMPL_ISUPPORTS1(MapsReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(MapsReporter, nsIMemoryReporter)
 
 MapsReporter::MapsReporter()
   : mSearchedForLibxul(false)
   , mMozillaLibraries(ArrayLength(mozillaLibraries))
 {
   const uint32_t len = ArrayLength(mozillaLibraries);
   for (uint32_t i = 0; i < len; i++) {
     nsAutoCString str;
     str.Assign(mozillaLibraries[i]);
     mMozillaLibraries.PutEntry(str);
   }
 }
 
 NS_IMETHODIMP
-MapsReporter::CollectReports(nsIMemoryMultiReporterCallback *aCb,
+MapsReporter::CollectReports(nsIMemoryReporterCallback *aCb,
                              nsISupports *aClosure)
 {
   CategoriesSeen categoriesSeen;
 
   FILE *f = fopen("/proc/self/smaps", "r");
   if (!f)
     return NS_ERROR_FAILURE;
 
@@ -253,17 +253,17 @@ MapsReporter::FindLibxul()
 
   fclose(f);
   return mLibxulDir.IsEmpty() ? NS_ERROR_FAILURE : NS_OK;
 }
 
 nsresult
 MapsReporter::ParseMapping(
   FILE *aFile,
-  nsIMemoryMultiReporterCallback *aCb,
+  nsIMemoryReporterCallback *aCb,
   nsISupports *aClosure,
   CategoriesSeen *aCategoriesSeen)
 {
   // We need to use native types in order to get good warnings from fscanf, so
   // let's make sure that the native types have the sizes we expect.
   static_assert(sizeof(long long) == sizeof(int64_t),
                 "size of (long long) is expected to match (int64_t)");
   static_assert(sizeof(int) == sizeof(int32_t),
@@ -456,17 +456,17 @@ MapsReporter::GetReporterNameAndDescript
   aDesc.Append(")");
 }
 
 nsresult
 MapsReporter::ParseMapBody(
   FILE *aFile,
   const nsACString &aName,
   const nsACString &aDescription,
-  nsIMemoryMultiReporterCallback *aCb,
+  nsIMemoryReporterCallback *aCb,
   nsISupports *aClosure,
   CategoriesSeen *aCategoriesSeen)
 {
   static_assert(sizeof(long long) == sizeof(int64_t),
                 "size of (long long) is expected to match (int64_t)");
 
   const int argCount = 2;
 
@@ -515,39 +515,39 @@ MapsReporter::ParseMapBody(
                      nsIMemoryReporter::UNITS_BYTES,
                      int64_t(size) * 1024, // convert from kB to bytes
                      aDescription, aClosure);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
-class ResidentUniqueReporter MOZ_FINAL : public MemoryReporterBase
+class ResidentUniqueReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
   ResidentUniqueReporter()
-    : MemoryReporterBase("resident-unique", KIND_OTHER, UNITS_BYTES,
+    : MemoryUniReporter("resident-unique", KIND_OTHER, UNITS_BYTES,
 "Memory mapped by the process that is present in physical memory and not "
 "shared with any other processes.  This is also known as the process's unique "
 "set size (USS).  This is the amount of RAM we'd expect to be freed if we "
 "closed this process.")
   {}
 
 private:
   NS_IMETHOD GetAmount(int64_t *aAmount)
   {
     // You might be tempted to calculate USS by subtracting the "shared" value
     // from the "resident" value in /proc/<pid>/statm.  But at least on Linux,
     // statm's "shared" value actually counts pages backed by files, which has
     // little to do with whether the pages are actually shared.  smaps on the
     // other hand appears to give us the correct information.
     //
-    // We could calculate this data during the smaps multi-reporter, but the
-    // overhead of the smaps reporter is considerable (we don't even run the
-    // smaps reporter in normal about:memory operation).  Hopefully this
+    // We could calculate this data within the smaps reporter, but the overhead
+    // of the smaps reporter is considerable (we don't even run the smaps
+    // reporter in normal about:memory operation).  Hopefully this
     // implementation is fast enough not to matter.
 
     *aAmount = 0;
 
     FILE *f = fopen("/proc/self/smaps", "r");
     NS_ENSURE_STATE(f);
 
     int64_t total = 0;
@@ -563,16 +563,16 @@ private:
 
     fclose(f);
     return NS_OK;
   }
 };
 
 void Init()
 {
-  nsCOMPtr<nsIMemoryMultiReporter> reporter = new MapsReporter();
-  NS_RegisterMemoryMultiReporter(reporter);
+  nsCOMPtr<nsIMemoryReporter> reporter = new MapsReporter();
+  NS_RegisterMemoryReporter(reporter);
 
   NS_RegisterMemoryReporter(new ResidentUniqueReporter());
 }
 
 } // namespace MapsMemoryReporter
 } // namespace mozilla
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -929,17 +929,17 @@ class nsCycleCollector
 
     // mVisitedRefCounted and mVisitedGCed are only used for telemetry
     uint32_t mVisitedRefCounted;
     uint32_t mVisitedGCed;
 
     CC_BeforeUnlinkCallback mBeforeUnlinkCB;
     CC_ForgetSkippableCallback mForgetSkippableCB;
 
-    nsCOMPtr<nsIMemoryMultiReporter> mReporter;
+    nsCOMPtr<nsIMemoryReporter> mReporter;
 
     nsPurpleBuffer mPurpleBuf;
 
     uint32_t mUnmergedNeeded;
     uint32_t mMergedInARow;
 
 public:
     nsCycleCollector();
@@ -2402,32 +2402,32 @@ nsCycleCollector::CollectWhite()
     return count > 0;
 }
 
 
 ////////////////////////
 // Memory reporter
 ////////////////////////
 
-class CycleCollectorMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter
+class CycleCollectorReporter MOZ_FINAL : public nsIMemoryReporter
 {
   public:
-    CycleCollectorMultiReporter(nsCycleCollector* aCollector)
+    CycleCollectorReporter(nsCycleCollector* aCollector)
       : mCollector(aCollector)
     {}
 
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD GetName(nsACString& name)
     {
         name.AssignLiteral("cycle-collector");
         return NS_OK;
     }
 
-    NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback* aCb,
+    NS_IMETHOD CollectReports(nsIMemoryReporterCallback* aCb,
                               nsISupports* aClosure)
     {
         size_t objectSize, graphNodesSize, graphEdgesSize, weakMapsSize,
             whiteNodesSize, purpleBufferSize;
         mCollector->SizeOfIncludingThis(MallocSizeOf,
                                         &objectSize,
                                         &graphNodesSize, &graphEdgesSize,
                                         &weakMapsSize,
@@ -2476,17 +2476,17 @@ class CycleCollectorMultiReporter MOZ_FI
     }
 
   private:
     NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MallocSizeOf)
 
     nsCycleCollector* mCollector;
 };
 
-NS_IMPL_ISUPPORTS1(CycleCollectorMultiReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(CycleCollectorReporter, nsIMemoryReporter)
 
 
 ////////////////////////////////////////////////////////////////////////
 // Collector implementation
 ////////////////////////////////////////////////////////////////////////
 
 nsCycleCollector::nsCycleCollector() :
     mCollectionInProgress(false),
@@ -2503,33 +2503,33 @@ nsCycleCollector::nsCycleCollector() :
     mReporter(nullptr),
     mUnmergedNeeded(0),
     mMergedInARow(0)
 {
 }
 
 nsCycleCollector::~nsCycleCollector()
 {
-    NS_UnregisterMemoryMultiReporter(mReporter);
+    NS_UnregisterMemoryReporter(mReporter);
 }
 
 void
 nsCycleCollector::RegisterJSRuntime(CycleCollectedJSRuntime *aJSRuntime)
 {
     if (mJSRuntime)
         Fault("multiple registrations of cycle collector JS runtime", aJSRuntime);
 
     mJSRuntime = aJSRuntime;
 
     // We can't register the reporter in nsCycleCollector() because that runs
     // before the memory reporter manager is initialized.  So we do it here
     // instead.
     static bool registered = false;
     if (!registered) {
-        NS_RegisterMemoryMultiReporter(new CycleCollectorMultiReporter(this));
+        NS_RegisterMemoryReporter(new CycleCollectorReporter(this));
         registered = true;
     }
 }
 
 void
 nsCycleCollector::ForgetJSRuntime()
 {
     if (!mJSRuntime)
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -12,65 +12,32 @@ interface nsICancelableRunnable;
 
 /*
  * Memory reporters measure Firefox's memory usage.  They are mainly used to
  * generate the about:memory page.  You should read
  * https://wiki.mozilla.org/Memory_Reporting before writing a memory
  * reporter.
  */
 
-/*
- * An nsIMemoryReporter reports a single memory measurement as an object.
- * Use this when it makes sense to gather this measurement without gathering
- * related measurements at the same time.
- *
- * Note that the |amount| field may be implemented as a function, and so
- * accessing it can trigger significant computation;  the other fields can
- * be accessed without triggering this computation.  (Compare and contrast
- * this with nsIMemoryMultiReporter.)  
- *
- * aboutMemory.js is the most important consumer of memory reports.  It
- * places the following constraints on reports.
- *
- * - There must be an "explicit" tree.  It represents non-overlapping
- *   regions of memory that have been explicitly allocated with an
- *   OS-level allocation (e.g. mmap/VirtualAlloc/vm_allocate) or a
- *   heap-level allocation (e.g. malloc/calloc/operator new).  Reporters
- *   in this tree must have kind HEAP or NONHEAP, units BYTES, and a
- *   description that is a sentence (i.e. starts with a capital letter and
- *   ends with a period, or similar).
- *
- * - The "size", "rss", "pss" and "swap" trees are optional.  They
- *   represent regions of virtual memory that the process has mapped.
- *   Reporters in this category must have kind NONHEAP, units BYTES, and a
- *   non-empty description.
- *
- * - The "compartments" and "ghost-windows" trees are optional.  They are
- *   used by about:compartments.  Reporters in these trees must have kind
- *   OTHER, units COUNT, an amount of 1, and a description that's an empty
- *   string.
- *
- * - All other reports are unconstrained except that they must have a
- *   description that is a sentence.
- */
-[scriptable, uuid(b2c39f65-1799-4b92-a806-ab3cf6af3cfa)]
-interface nsIMemoryReporter : nsISupports
+[scriptable, function, uuid(3a61be3b-b93b-461a-a4f8-388214f558b1)]
+interface nsIMemoryReporterCallback : nsISupports
 {
   /*
-   * The name of the process containing this reporter.  Each reporter initially
-   * has "" in this field, indicating that it applies to the current process.
-   * (This is true even for reporters in a child process.)  When a reporter
-   * from a child process is copied into the main process, the copy has its
-   * 'process' field set appropriately.
-   */
-  readonly attribute ACString process;
-
-  /*
-   * The path that this memory usage should be reported under.  Paths are
-   * '/'-delimited, eg. "a/b/c".  
+   * The arguments to the callback are as follows.
+   *
+   *
+   * |process|  The name of the process containing this reporter.  Each
+   * reporter initially has "" in this field, indicating that it applies to the
+   * current process.  (This is true even for reporters in a child process.)
+   * When a reporter from a child process is copied into the main process, the
+   * copy has its 'process' field set appropriately.
+   *
+   *
+   * |path|  The path that this memory usage should be reported under.  Paths
+   * are '/'-delimited, eg. "a/b/c".
    *
    * Each reporter can be viewed as representing a leaf node in a tree.
    * Internal nodes of the tree don't have reporters.  So, for example, the
    * reporters "explicit/a/b", "explicit/a/c", "explicit/d/e", and
    * "explicit/d/f" define this tree:
    *
    *   explicit
    *   |--a
@@ -93,21 +60,19 @@ interface nsIMemoryReporter : nsISupport
    * If you want to include a '/' not as a path separator, e.g. because the
    * path contains a URL, you need to convert each '/' in the URL to a '\'.
    * Consumers of the path will undo this change.  Any other '\' character
    * in a path will also be changed.  This is clumsy but hasn't caused any
    * problems so far.
    *
    * The paths of all reporters form a set of trees.  Trees can be
    * "degenerate", i.e. contain a single entry with no '/'.
-   */
-  readonly attribute AUTF8String path;
-
-  /*
-   * There are three kinds of memory reporters.
+   *
+   *
+   * |kind|  There are three kinds of memory reporters.
    *
    *  - HEAP: reporters measuring memory allocated by the heap allocator,
    *    e.g. by calling malloc, calloc, realloc, memalign, operator new, or
    *    operator new[].  Reporters in this category must have units
    *    UNITS_BYTES.
    *
    *  - NONHEAP: reporters measuring memory which the program explicitly
    *    allocated, but does not live on the heap.  Such memory is commonly
@@ -115,29 +80,19 @@ interface nsIMemoryReporter : nsISupport
    *    mmap, VirtualAlloc, or vm_allocate).  Reporters in this category
    *    must have units UNITS_BYTES.
    *
    *  - OTHER: reporters which don't fit into either of these categories.
    *    They can have any units.
    *
    * The kind only matters for reporters in the "explicit" tree;
    * aboutMemory.js uses it to calculate "heap-unclassified".
-   */
-  const int32_t KIND_NONHEAP = 0;
-  const int32_t KIND_HEAP    = 1;
-  const int32_t KIND_OTHER   = 2;
-
-  /*
-   * The reporter kind.  See KIND_* above.
-   */
-  readonly attribute int32_t kind;
-
-  /*
-   * The amount reported by a memory reporter must have one of the following
-   * units, but you may of course add new units as necessary:
+   *
+   *
+   * |units|  The units on the reporter's amount.  One of the following.
    *
    *  - BYTES: The amount contains a number of bytes.
    *
    *  - COUNT: The amount is an instantaneous count of things currently in
    *    existence.  For instance, the number of tabs currently open would have
    *    units COUNT.
    *
    *  - COUNT_CUMULATIVE: The amount contains the number of times some event
@@ -151,122 +106,119 @@ interface nsIMemoryReporter : nsISupport
    *  - PERCENTAGE: The amount contains a fraction that should be expressed as
    *    a percentage.  NOTE!  The |amount| field should be given a value 100x
    *    the actual percentage;  this number will be divided by 100 when shown.
    *    This allows a fractional percentage to be shown even though |amount| is
    *    an integer.  E.g. if the actual percentage is 12.34%, |amount| should
    *    be 1234.
    *
    *    Values greater than 100% are allowed.
-   */
-  const int32_t UNITS_BYTES = 0;
-  const int32_t UNITS_COUNT = 1;
-  const int32_t UNITS_COUNT_CUMULATIVE = 2;
-  const int32_t UNITS_PERCENTAGE = 3;
-
-  /*
-   * The units on the reporter's amount.  See UNITS_* above.
+   *
+   *
+   * |amount|  The numeric value reported by this memory reporter.  Accesses
+   * can fail if something goes wrong when getting the amount.
+   *
+   *
+   * |description|  A human-readable description of this memory usage report.
    */
-  readonly attribute int32_t units;
-
-  /*
-   * The numeric value reported by this memory reporter.  Accesses can fail if
-   * something goes wrong when getting the amount.
-   */
-  readonly attribute int64_t amount;
-
-  /*
-   * A human-readable description of this memory usage report.
-   */
-  readonly attribute AUTF8String description;
-};
-
-[scriptable, function, uuid(5b15f3fa-ba15-443c-8337-7770f5f0ce5d)]
-interface nsIMemoryMultiReporterCallback : nsISupports
-{
   void callback(in ACString process, in AUTF8String path, in int32_t kind,
                 in int32_t units, in int64_t amount,
                 in AUTF8String description, in nsISupports closure);
 };
 
 /*
- * An nsIMemoryMultiReporter reports multiple memory measurements via a
- * callback function which is called once for each measurement.  Use this
- * when you want to gather multiple measurements in a single operation (eg.
- * a single traversal of a large data structure).
+ * An nsIMemoryReporter reports one or more memory measurements via a
+ * callback function which is called once for each measurement.
+ *
+ * An nsIMemoryReporter that reports a single measurement is sometimes called a
+ * "uni-reporter".  One that reports multiple measurements is sometimes called
+ * a "multi-reporter".
+ *
+ * aboutMemory.js is the most important consumer of memory reports.  It
+ * places the following constraints on reports.
  *
- * The arguments to the callback deliberately match the fields in
- * nsIMemoryReporter, but note that seeing any of these arguments requires
- * calling collectReports which will trigger all relevant computation.
- * (Compare and contrast this with nsIMemoryReporter, which allows all
- * fields except |amount| to be accessed without triggering computation.)
+ * - There must be an "explicit" tree.  It represents non-overlapping
+ *   regions of memory that have been explicitly allocated with an
+ *   OS-level allocation (e.g. mmap/VirtualAlloc/vm_allocate) or a
+ *   heap-level allocation (e.g. malloc/calloc/operator new).  Reporters
+ *   in this tree must have kind HEAP or NONHEAP, units BYTES, and a
+ *   description that is a sentence (i.e. starts with a capital letter and
+ *   ends with a period, or similar).
+ *
+ * - The "size", "rss", "pss" and "swap" trees are optional.  They
+ *   represent regions of virtual memory that the process has mapped.
+ *   Reporters in this category must have kind NONHEAP, units BYTES, and a
+ *   non-empty description.
+ *
+ * - The "compartments" and "ghost-windows" trees are optional.  They are
+ *   used by about:compartments.  Reporters in these trees must have kind
+ *   OTHER, units COUNT, an amount of 1, and a description that's an empty
+ *   string.
+ *
+ * - All other reports are unconstrained except that they must have a
+ *   description that is a sentence.
  */
-[scriptable, uuid(24d61ead-237b-4969-a6bd-73fd8fed1d99)]
-interface nsIMemoryMultiReporter : nsISupports
+[scriptable, uuid(53248304-124b-43cd-99dc-6e5797b91618)]
+interface nsIMemoryReporter : nsISupports
 {
   /*
-   * The name of the multi-reporter.  Useful when only one multi-reporter
-   * needs to be run.  Must be unique;  if multi-reporters share names it's
-   * likely the wrong one will be called in certain circumstances.
+   * The name of the reporter.  Useful when only one reporter needs to be run.
+   * Must be unique;  if reporters share names it's likely the wrong one will
+   * be called in certain circumstances.
    */
   readonly attribute ACString name;
 
   /*
-   * Run the multi-reporter.
+   * Run the reporter.
+   */
+  void collectReports(in nsIMemoryReporterCallback callback,
+                      in nsISupports closure);
+
+  /*
+   * Kinds.  See the |kind| comment in nsIMemoryReporterCallback.
    */
-  void collectReports(in nsIMemoryMultiReporterCallback callback,
-                      in nsISupports closure);
+  const int32_t KIND_NONHEAP = 0;
+  const int32_t KIND_HEAP    = 1;
+  const int32_t KIND_OTHER   = 2;
+
+  /*
+   * Units.  See the |units| comment in nsIMemoryReporterCallback.
+   */
+  const int32_t UNITS_BYTES = 0;
+  const int32_t UNITS_COUNT = 1;
+  const int32_t UNITS_COUNT_CUMULATIVE = 2;
+  const int32_t UNITS_PERCENTAGE = 3;
 };
 
-[scriptable, builtinclass, uuid(70b0e608-8dbf-4dc7-b88f-f1c745c1b48c)]
+[scriptable, builtinclass, uuid(4db7040a-16f9-4249-879b-fe72729c7ef5)]
 interface nsIMemoryReporterManager : nsISupports
 {
   /*
    * Return an enumerator of nsIMemoryReporters that are currently registered.
    */
   nsISimpleEnumerator enumerateReporters ();
 
   /*
-   * Return an enumerator of nsIMemoryMultiReporters that are currently
-   * registered.
-   */
-  nsISimpleEnumerator enumerateMultiReporters ();
-
-  /*
    * Register the given nsIMemoryReporter.  After a reporter is registered,
    * it will be available via enumerateReporters().  The Manager service
    * will hold a strong reference to the given reporter.
    */
   void registerReporter (in nsIMemoryReporter reporter);
 
   /*
-   * Register the given nsIMemoryMultiReporter.  After a multi-reporter is
-   * registered, it will be available via enumerateMultiReporters().  The
-   * Manager service will hold a strong reference to the given
-   * multi-reporter.
-   */
-  void registerMultiReporter (in nsIMemoryMultiReporter reporter);
-
-  /*
    * Unregister the given memory reporter.
    */
   void unregisterReporter (in nsIMemoryReporter reporter);
 
-  /*
-   * Unregister the given memory multi-reporter.
-   */
-  void unregisterMultiReporter (in nsIMemoryMultiReporter reporter);
-
   /**
    * These functions should only be used for testing purposes.
    */
   void blockRegistration();
   void unblockRegistration();
   void registerReporterEvenIfBlocked(in nsIMemoryReporter aReporter);
-  void registerMultiReporterEvenIfBlocked(in nsIMemoryMultiReporter aReporter);
 
   /*
    * Initialize.
    */
   void init ();
 
   /*
    * Get the resident size (aka. RSS, physical memory used).  This reporter
@@ -275,18 +227,18 @@ interface nsIMemoryReporterManager : nsI
    */
   readonly attribute int64_t resident;
 
   /*
    * Get the total size of explicit memory allocations, both at the OS-level
    * (eg. via mmap, VirtualAlloc) and at the heap level (eg. via malloc,
    * calloc, operator new).  (Nb: it covers all heap allocations, but will
    * miss any OS-level ones not covered by memory reporters.)  This reporter
-   * is special-cased because it's interesting, and is moderately difficult
-   * to compute in JS.  Accesses can fail.
+   * is special-cased because it's interesting, and is difficult to compute
+   * from JavaScript code.  Accesses can fail.
    */
   readonly attribute int64_t explicit;
 
   /*
    * This attribute indicates if moz_malloc_usable_size() works.
    */
   [infallible] readonly attribute boolean hasMozMallocUsableSize;
 
@@ -302,20 +254,17 @@ interface nsIMemoryReporterManager : nsI
 
 #include "nsStringGlue.h"
 
 // Note that the memory reporters are held in an nsCOMArray, which means
 // that individual reporters should be referenced with |nsIMemoryReporter *|
 // instead of nsCOMPtr<nsIMemoryReporter>.
 
 XPCOM_API(nsresult) NS_RegisterMemoryReporter(nsIMemoryReporter* aReporter);
-XPCOM_API(nsresult) NS_RegisterMemoryMultiReporter(nsIMemoryMultiReporter* aReporter);
-
 XPCOM_API(nsresult) NS_UnregisterMemoryReporter(nsIMemoryReporter* aReporter);
-XPCOM_API(nsresult) NS_UnregisterMemoryMultiReporter(nsIMemoryMultiReporter* aReporter);
 
 #if defined(MOZ_DMD)
 namespace mozilla {
 namespace dmd {
 // This runs all the memory reporters but does nothing with the results;  i.e.
 // it does the minimal amount of work possible for DMD to do its thing.
 void RunReporters();
 }
@@ -370,95 +319,83 @@ void RunReporters();
   static size_t fn(const void* aPtr)                                          \
   {                                                                           \
       return moz_malloc_size_of(aPtr);                                        \
   }
 
 namespace mozilla {
 
 // The following base class reduces the amount of boilerplate code required for
-// memory reporters.  You just need to provide the following.
-// - The constant values: path, kind, units, and description.  They are passed
-//   to the MemoryReporterBase constructor.
-// - A (private) Amount() or (public) GetAmount() method.  It can use the 
+// memory uni-reporters.  You just need to provide the following.
+// - The constant values: nameAndPath (which serves as both the reporters name,
+//   and the path in its single report), kind, units, and description.  They
+//   are passed to the MemoryUniReporter constructor.
+// - A (private) Amount() or (public) GetAmount() method.  It can use the
 //   MallocSizeOf method if necessary.  (There is also
 //   MallocSizeOfOn{Alloc,Free}, which can be useful.)  Use Amount() if the
 //   reporter is infallible, and GetAmount() otherwise.  (If you fail to
 //   provide one or the other, you'll get assertion failures when the memory
 //   reporter runs.)
 //
 // The class name of subclasses should match the path, minus the "explicit"
 // (if present), and with "Reporter" at the end.  For example:
 // - "explicit/dom/xyzzy"     --> DOMXyzzyReporter
 // - "js-compartments/system" --> JSCompartmentsSystemReporter
 //
-class MemoryReporterBase : public nsIMemoryReporter
+class MemoryUniReporter : public nsIMemoryReporter
 {
 public:
-  MemoryReporterBase(const char* aPath, int32_t aKind, int32_t aUnits,
-                     const char* aDescription)
-    : mPath(aPath)
+  MemoryUniReporter(const char* aNameAndPath, int32_t aKind, int32_t aUnits,
+                    const char* aDescription)
+    : mNameAndPath(aNameAndPath)
     , mKind(aKind)
     , mUnits(aUnits)
     , mDescription(aDescription)
   {}
 
-  virtual ~MemoryReporterBase() {}
+  virtual ~MemoryUniReporter() {}
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
-  NS_IMETHOD GetProcess(nsACString& aProcess)
+  NS_IMETHOD GetName(nsACString& aName)
   {
-    aProcess.Truncate();
+    aName.Assign(mNameAndPath);
     return NS_OK;
   }
 
-  NS_IMETHOD GetPath(nsACString& aPath)
+  NS_IMETHOD CollectReports(nsIMemoryReporterCallback* aCallback,
+                            nsISupports* aClosure)
   {
-    aPath.Assign(mPath);
-    return NS_OK;
+    int64_t amount;
+    nsresult rv = GetAmount(&amount);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return aCallback->Callback(EmptyCString(), mNameAndPath, mKind, mUnits,
+                               amount, mDescription, aClosure);
   }
 
-  NS_IMETHOD GetKind(int32_t* aKind)
-  {
-    *aKind = mKind;
-    return NS_OK;
-  }
-
-  NS_IMETHOD GetUnits(int32_t* aUnits)
-  {
-    *aUnits = mUnits;
-    return NS_OK;
-  }
-
+protected:
   NS_IMETHOD GetAmount(int64_t* aAmount)
   {
     *aAmount = Amount();
     return NS_OK;
   }
 
-  NS_IMETHOD GetDescription(nsACString& aDescription)
-  {
-    aDescription.Assign(mDescription);
-    return NS_OK;
-  }
-
-protected:
   virtual int64_t Amount()
   {
     // We only reach here if neither GetAmount() nor Amount() was overridden.
     MOZ_ASSERT(false);
     return 0;
   }
 
   NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(MallocSizeOf)
   NS_MEMORY_REPORTER_MALLOC_SIZEOF_ON_ALLOC_FUN(MallocSizeOfOnAlloc)
   NS_MEMORY_REPORTER_MALLOC_SIZEOF_ON_FREE_FUN(MallocSizeOfOnFree)
 
-  const nsCString mPath;
+  const nsCString mNameAndPath;
   const int32_t   mKind;
   const int32_t   mUnits;
   const nsCString mDescription;
 };
 
 } // namespace mozilla
 
 
--- a/xpcom/base/nsMemoryInfoDumper.cpp
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -667,42 +667,45 @@ DumpReport(nsIGZFileWriter *aWriter, boo
   description.ReplaceSubstring("\n", "\\n");     // <newline> --> \n
   DUMP(aWriter, ", \"description\": \"");
   DUMP(aWriter, description);
   DUMP(aWriter, "\"}");
 
   return NS_OK;
 }
 
-class DumpMultiReporterCallback MOZ_FINAL : public nsIMemoryMultiReporterCallback
+class DumpReporterCallback MOZ_FINAL : public nsIMemoryReporterCallback
 {
-  public:
-    NS_DECL_ISUPPORTS
+public:
+  NS_DECL_ISUPPORTS
+
+  DumpReporterCallback(bool* aIsFirstPtr) : mIsFirstPtr(aIsFirstPtr) {}
 
-      NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
-          int32_t aKind, int32_t aUnits, int64_t aAmount,
-          const nsACString &aDescription,
-          nsISupports *aData)
-      {
-        nsCOMPtr<nsIGZFileWriter> writer = do_QueryInterface(aData);
-        NS_ENSURE_TRUE(writer, NS_ERROR_FAILURE);
+  NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
+      int32_t aKind, int32_t aUnits, int64_t aAmount,
+      const nsACString &aDescription,
+      nsISupports *aData)
+  {
+    nsCOMPtr<nsIGZFileWriter> writer = do_QueryInterface(aData);
+    NS_ENSURE_TRUE(writer, NS_ERROR_FAILURE);
 
-        // The |isFirst = false| assumes that at least one single reporter is
-        // present and so will have been processed in
-        // DumpProcessMemoryReportsToGZFileWriter() below.
-        return DumpReport(writer, /* isFirst = */ false, aProcess, aPath,
-            aKind, aUnits, aAmount, aDescription);
-        return NS_OK;
-      }
+    // The |isFirst = false| assumes that at least one single reporter is
+    // present and so will have been processed in
+    // DumpProcessMemoryReportsToGZFileWriter() below.
+    bool isFirst = *mIsFirstPtr;
+    *mIsFirstPtr = false;
+    return DumpReport(writer, isFirst, aProcess, aPath, aKind, aUnits, aAmount,
+                      aDescription);
+  }
+
+private:
+  bool* mIsFirstPtr;
 };
 
-NS_IMPL_ISUPPORTS1(
-    DumpMultiReporterCallback
-    , nsIMemoryMultiReporterCallback
-    )
+NS_IMPL_ISUPPORTS1(DumpReporterCallback, nsIMemoryReporterCallback)
 
 } // namespace mozilla
 
 static void
 MakeFilename(const char *aPrefix, const nsAString &aIdentifier,
              const char *aSuffix, nsACString &aResult)
 {
   aResult = nsPrintfCString("%s-%s-%d.%s",
@@ -790,18 +793,16 @@ DMDWrite(void* aState, const char* aFmt,
   vsnprintf(state->mBuf, state->kBufSize, aFmt, ap);
   unused << state->mGZWriter->Write(state->mBuf);
 }
 #endif
 
 static nsresult
 DumpProcessMemoryReportsToGZFileWriter(nsIGZFileWriter *aWriter)
 {
-  nsresult rv;
-
   // Increment this number if the format changes.
   //
   // This is the first write to the file, and it causes |aWriter| to allocate
   // over 200 KiB of memory.
   //
   DUMP(aWriter, "{\n  \"version\": 1,\n");
 
   DUMP(aWriter, "  \"hasMozMallocUsableSize\": ");
@@ -809,63 +810,25 @@ DumpProcessMemoryReportsToGZFileWriter(n
   nsCOMPtr<nsIMemoryReporterManager> mgr =
     do_GetService("@mozilla.org/memory-reporter-manager;1");
   NS_ENSURE_STATE(mgr);
 
   DUMP(aWriter, mgr->GetHasMozMallocUsableSize() ? "true" : "false");
   DUMP(aWriter, ",\n");
   DUMP(aWriter, "  \"reports\": ");
 
-  // Process single reporters.
+  // Process reporters.
   bool isFirst = true;
   bool more;
   nsCOMPtr<nsISimpleEnumerator> e;
   mgr->EnumerateReporters(getter_AddRefs(e));
+  nsRefPtr<DumpReporterCallback> cb = new DumpReporterCallback(&isFirst);
   while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
     nsCOMPtr<nsIMemoryReporter> r;
     e->GetNext(getter_AddRefs(r));
-
-    nsCString process;
-    rv = r->GetProcess(process);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCString path;
-    rv = r->GetPath(path);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    int32_t kind;
-    rv = r->GetKind(&kind);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    int32_t units;
-    rv = r->GetUnits(&units);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    int64_t amount;
-    rv = r->GetAmount(&amount);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCString description;
-    rv = r->GetDescription(description);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = DumpReport(aWriter, isFirst, process, path, kind, units, amount,
-                    description);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    isFirst = false;
-  }
-
-  // Process multi-reporters.
-  nsCOMPtr<nsISimpleEnumerator> e2;
-  mgr->EnumerateMultiReporters(getter_AddRefs(e2));
-  nsRefPtr<DumpMultiReporterCallback> cb = new DumpMultiReporterCallback();
-  while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
-    nsCOMPtr<nsIMemoryMultiReporter> r;
-    e2->GetNext(getter_AddRefs(r));
     r->CollectReports(cb, aWriter);
   }
 
   DUMP(aWriter, "\n  ]\n}\n");
 
   return NS_OK;
 }
 
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -322,21 +322,21 @@ static nsresult GetResident(int64_t* aN)
 }
 
 static nsresult GetResidentFast(int64_t* aN)
 {
     return GetResident(aN);
 }
 
 #define HAVE_PRIVATE_REPORTER
-class PrivateReporter MOZ_FINAL : public MemoryReporterBase
+class PrivateReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     PrivateReporter()
-      : MemoryReporterBase("private", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("private", KIND_OTHER, UNITS_BYTES,
 "Memory that cannot be shared with other processes, including memory that is "
 "committed and marked MEM_PRIVATE, data that is not mapped, and executable "
 "pages that have been written to.")
     {}
 
     NS_IMETHOD GetAmount(int64_t* aAmount)
     {
         PROCESS_MEMORY_COUNTERS_EX pmcex;
@@ -351,54 +351,54 @@ public:
         *aAmount = pmcex.PrivateUsage;
         return NS_OK;
     }
 };
 
 #endif  // XP_<PLATFORM>
 
 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
-class VsizeReporter MOZ_FINAL : public MemoryReporterBase
+class VsizeReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     VsizeReporter()
-      : MemoryReporterBase("vsize", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("vsize", KIND_OTHER, UNITS_BYTES,
 "Memory mapped by the process, including code and data segments, the heap, "
 "thread stacks, memory explicitly mapped by the process via mmap and similar "
 "operations, and memory shared with other processes. This is the vsize figure "
 "as reported by 'top' and 'ps'.  This figure is of limited use on Mac, where "
 "processes share huge amounts of memory with one another.  But even on other "
 "operating systems, 'resident' is a much better measure of the memory "
 "resources used by the process.")
     {}
 
     NS_IMETHOD GetAmount(int64_t* aAmount) { return GetVsize(aAmount); }
 };
 
-class ResidentReporter MOZ_FINAL : public MemoryReporterBase
+class ResidentReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     ResidentReporter()
-      : MemoryReporterBase("resident", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("resident", KIND_OTHER, UNITS_BYTES,
 "Memory mapped by the process that is present in physical memory, also known "
 "as the resident set size (RSS).  This is the best single figure to use when "
 "considering the memory resources used by the process, but it depends both on "
 "other processes being run and details of the OS kernel and so is best used "
 "for comparing the memory usage of a single process at different points in "
 "time.")
     {}
 
     NS_IMETHOD GetAmount(int64_t* aAmount) { return GetResident(aAmount); }
 };
 
-class ResidentFastReporter MOZ_FINAL : public MemoryReporterBase
+class ResidentFastReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     ResidentFastReporter()
-      : MemoryReporterBase("resident-fast", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("resident-fast", KIND_OTHER, UNITS_BYTES,
 "This is the same measurement as 'resident', but it tries to be as fast as "
 "possible at the expense of accuracy.  On most platforms this is identical to "
 "the 'resident' measurement, but on Mac it may over-count.  You should use "
 "'resident-fast' where you care about latency of collection (e.g. in "
 "telemetry).  Otherwise you should use 'resident'.")
     {}
 
     NS_IMETHOD GetAmount(int64_t* aAmount) { return GetResidentFast(aAmount); }
@@ -407,21 +407,21 @@ public:
 
 #ifdef XP_UNIX
 
 #include <sys/time.h>
 #include <sys/resource.h>
 
 #define HAVE_PAGE_FAULT_REPORTERS 1
 
-class PageFaultsSoftReporter MOZ_FINAL : public MemoryReporterBase
+class PageFaultsSoftReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     PageFaultsSoftReporter()
-      : MemoryReporterBase("page-faults-soft", KIND_OTHER,
+      : MemoryUniReporter("page-faults-soft", KIND_OTHER,
                            UNITS_COUNT_CUMULATIVE,
 "The number of soft page faults (also known as 'minor page faults') that "
 "have occurred since the process started.  A soft page fault occurs when the "
 "process tries to access a page which is present in physical memory but is "
 "not mapped into the process's address space.  For instance, a process might "
 "observe soft page faults when it loads a shared library which is already "
 "present in physical memory. A process may experience many thousands of soft "
 "page faults even when the machine has plenty of available physical memory, "
@@ -436,21 +436,21 @@ public:
         if (err != 0) {
             return NS_ERROR_FAILURE;
         }
         *aAmount = usage.ru_minflt;
         return NS_OK;
     }
 };
 
-class PageFaultsHardReporter MOZ_FINAL : public MemoryReporterBase
+class PageFaultsHardReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     PageFaultsHardReporter()
-      : MemoryReporterBase("page-faults-hard", KIND_OTHER,
+      : MemoryUniReporter("page-faults-hard", KIND_OTHER,
                            UNITS_COUNT_CUMULATIVE,
 "The number of hard page faults (also known as 'major page faults') that have "
 "occurred since the process started.  A hard page fault occurs when a process "
 "tries to access a page which is not present in physical memory. The "
 "operating system must access the disk in order to fulfill a hard page fault. "
 "When memory is plentiful, you should see very few hard page faults. But if "
 "the process tries to use more memory than your machine has available, you "
 "may see many thousands of hard page faults. Because accessing the disk is up "
@@ -474,122 +474,122 @@ public:
 /**
  ** memory reporter implementation for jemalloc and OSX malloc,
  ** to obtain info on total memory in use (that we know about,
  ** at least -- on OSX, there are sometimes other zones in use).
  **/
 
 #ifdef HAVE_JEMALLOC_STATS
 
-class HeapAllocatedReporter MOZ_FINAL : public MemoryReporterBase
+class HeapAllocatedReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     HeapAllocatedReporter()
-      : MemoryReporterBase("heap-allocated", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("heap-allocated", KIND_OTHER, UNITS_BYTES,
 "Memory mapped by the heap allocator that is currently allocated to the "
 "application.  This may exceed the amount of memory requested by the "
 "application because the allocator regularly rounds up request sizes. (The "
 "exact amount requested is not recorded.)")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return (int64_t) stats.allocated;
     }
 };
 
-class HeapOverheadWasteReporter MOZ_FINAL : public MemoryReporterBase
+class HeapOverheadWasteReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     // We mark this and the other heap-overhead reporters as KIND_NONHEAP
     // because KIND_HEAP memory means "counted in heap-allocated", which this
     // is not.
     HeapOverheadWasteReporter()
-      : MemoryReporterBase("explicit/heap-overhead/waste",
+      : MemoryUniReporter("explicit/heap-overhead/waste",
                            KIND_NONHEAP, UNITS_BYTES,
 "Committed bytes which do not correspond to an active allocation and which the "
 "allocator is not intentionally keeping alive (i.e., not 'heap-bookkeeping' or "
 "'heap-page-cache').  Although the allocator will waste some space under any "
 "circumstances, a large value here may indicate that the heap is highly "
 "fragmented, or that allocator is performing poorly for some other reason.")
     {}
 private:
     int64_t Amount()
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return stats.waste;
     }
 };
 
-class HeapOverheadBookkeepingReporter MOZ_FINAL : public MemoryReporterBase
+class HeapOverheadBookkeepingReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     HeapOverheadBookkeepingReporter()
-      : MemoryReporterBase("explicit/heap-overhead/bookkeeping",
+      : MemoryUniReporter("explicit/heap-overhead/bookkeeping",
                            KIND_NONHEAP, UNITS_BYTES,
 "Committed bytes which the heap allocator uses for internal data structures.")
     {}
 private:
     int64_t Amount()
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return stats.bookkeeping;
     }
 };
 
-class HeapOverheadPageCacheReporter MOZ_FINAL : public MemoryReporterBase
+class HeapOverheadPageCacheReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     HeapOverheadPageCacheReporter()
-      : MemoryReporterBase("explicit/heap-overhead/page-cache",
+      : MemoryUniReporter("explicit/heap-overhead/page-cache",
                            KIND_NONHEAP, UNITS_BYTES,
 "Memory which the allocator could return to the operating system, but hasn't. "
 "The allocator keeps this memory around as an optimization, so it doesn't "
 "have to ask the OS the next time it needs to fulfill a request. This value "
 "is typically not larger than a few megabytes.")
     {}
 private:
     int64_t Amount()
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return (int64_t) stats.page_cache;
     }
 };
 
-class HeapCommittedReporter MOZ_FINAL : public MemoryReporterBase
+class HeapCommittedReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     HeapCommittedReporter()
-      : MemoryReporterBase("heap-committed", KIND_OTHER, UNITS_BYTES,
+      : MemoryUniReporter("heap-committed", KIND_OTHER, UNITS_BYTES,
 "Memory mapped by the heap allocator that is committed, i.e. in physical "
 "memory or paged to disk.  This value corresponds to 'heap-allocated' + "
 "'heap-waste' + 'heap-bookkeeping' + 'heap-page-cache', but because "
 "these values are read at different times, the result probably won't match "
 "exactly.")
     {}
 private:
     int64_t Amount()
     {
         jemalloc_stats_t stats;
         jemalloc_stats(&stats);
         return (int64_t) (stats.allocated + stats.waste +
                           stats.bookkeeping + stats.page_cache);
     }
 };
 
-class HeapOverheadRatioReporter MOZ_FINAL : public MemoryReporterBase
+class HeapOverheadRatioReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     HeapOverheadRatioReporter()
-      : MemoryReporterBase("heap-overhead-ratio", KIND_OTHER,
+      : MemoryUniReporter("heap-overhead-ratio", KIND_OTHER,
                            UNITS_PERCENTAGE,
 "Ratio of committed, unused bytes to allocated bytes; i.e., "
 "'heap-overhead' / 'heap-allocated'.  This measures the overhead of "
 "the heap allocator relative to amount of memory allocated.")
     {}
 private:
     int64_t Amount()
     {
@@ -602,47 +602,47 @@ private:
 };
 #endif  // HAVE_JEMALLOC_STATS
 
 // Why is this here?  At first glance, you'd think it could be defined and
 // registered with nsMemoryReporterManager entirely within nsAtomTable.cpp.
 // However, the obvious time to register it is when the table is initialized,
 // and that happens before XPCOM components are initialized, which means the
 // NS_RegisterMemoryReporter call fails.  So instead we do it here.
-class AtomTablesReporter MOZ_FINAL : public MemoryReporterBase
+class AtomTablesReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     AtomTablesReporter()
-      : MemoryReporterBase("explicit/atom-tables", KIND_HEAP, UNITS_BYTES,
+      : MemoryUniReporter("explicit/atom-tables", KIND_HEAP, UNITS_BYTES,
 "Memory used by the dynamic and static atoms tables.")
     {}
 private:
     int64_t Amount() { return NS_SizeOfAtomTablesIncludingThis(MallocSizeOf); }
 };
 
 #ifdef MOZ_DMD
 
 namespace mozilla {
 namespace dmd {
 
-class DMDMultiReporter MOZ_FINAL : public nsIMemoryMultiReporter
+class DMDReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
-  DMDMultiReporter()
+  DMDReporter()
   {}
 
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD GetName(nsACString& aName)
   {
     aName.Assign("dmd");
     return NS_OK;
   }
 
-  NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback* aCallback,
+  NS_IMETHOD CollectReports(nsIMemoryReporterCallback* aCallback,
                             nsISupports* aClosure)
   {
     dmd::Sizes sizes;
     dmd::SizeOf(&sizes);
 
 #define REPORT(_path, _amount, _desc)                                         \
     do {                                                                      \
       nsresult rv;                                                            \
@@ -672,17 +672,17 @@ public:
            "Memory used by DMD's live block table.");
 
 #undef REPORT
 
     return NS_OK;
   }
 };
 
-NS_IMPL_ISUPPORTS1(DMDMultiReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(DMDReporter, nsIMemoryReporter)
 
 } // namespace dmd
 } // namespace mozilla
 
 #endif  // MOZ_DMD
 
 /**
  ** nsMemoryReporterManager implementation
@@ -720,17 +720,17 @@ nsMemoryReporterManager::Init()
 
 #ifdef HAVE_PRIVATE_REPORTER
     RegisterReporter(new PrivateReporter);
 #endif
 
     RegisterReporter(new AtomTablesReporter);
 
 #ifdef MOZ_DMD
-    RegisterMultiReporter(new mozilla::dmd::DMDMultiReporter);
+    RegisterReporter(new mozilla::dmd::DMDReporter);
 #endif
 
 #if defined(XP_LINUX)
     nsMemoryInfoDumper::Initialize();
 #endif
 
     return NS_OK;
 }
@@ -820,33 +820,16 @@ nsMemoryReporterManager::EnumerateReport
     mozilla::MutexAutoLock autoLock(mMutex);
 
     nsRefPtr<HashtableEnumerator> enumerator =
         new HashtableEnumerator(mReporters);
     enumerator.forget(aResult);
     return NS_OK;
 }
 
-NS_IMETHODIMP
-nsMemoryReporterManager::EnumerateMultiReporters(nsISimpleEnumerator** aResult)
-{
-    // Memory multi-reporters are not necessarily threadsafe, so
-    // EnumerateMultiReporters() must be called from the main thread.
-    if (!NS_IsMainThread()) {
-        MOZ_CRASH();
-    }
-
-    mozilla::MutexAutoLock autoLock(mMutex);
-
-    nsRefPtr<HashtableEnumerator> enumerator =
-        new HashtableEnumerator(mMultiReporters);
-    enumerator.forget(aResult);
-    return NS_OK;
-}
-
 static void
 DebugAssertRefcountIsNonZero(nsISupports* aObj)
 {
 #ifdef DEBUG
     // This will probably crash if the object's refcount is 0.
     uint32_t refcnt = NS_ADDREF(aObj);
     MOZ_ASSERT(refcnt >= 2);
     NS_RELEASE(aObj);
@@ -892,80 +875,31 @@ nsMemoryReporterManager::RegisterReporte
 
 NS_IMETHODIMP
 nsMemoryReporterManager::RegisterReporterEvenIfBlocked(
     nsIMemoryReporter* aReporter)
 {
     return RegisterReporterHelper(aReporter, /* force = */ true);
 }
 
-nsresult
-nsMemoryReporterManager::RegisterMultiReporterHelper(
-    nsIMemoryMultiReporter* aReporter, bool aForce)
-{
-    // This method is thread-safe.
-    mozilla::MutexAutoLock autoLock(mMutex);
-
-    if ((mIsRegistrationBlocked && !aForce) ||
-         mMultiReporters.Contains(aReporter)) {
-        return NS_ERROR_FAILURE;
-    }
-
-    {
-        nsCOMPtr<nsIMemoryMultiReporter> kungFuDeathGrip = aReporter;
-        mMultiReporters.PutEntry(aReporter);
-    }
-
-    DebugAssertRefcountIsNonZero(aReporter);
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::RegisterMultiReporter(nsIMemoryMultiReporter* aReporter)
-{
-    return RegisterMultiReporterHelper(aReporter, /* force = */ false);
-}
-
-NS_IMETHODIMP
-nsMemoryReporterManager::RegisterMultiReporterEvenIfBlocked(
-    nsIMemoryMultiReporter* aReporter)
-{
-    return RegisterMultiReporterHelper(aReporter, /* force = */ true);
-}
-
 NS_IMETHODIMP
 nsMemoryReporterManager::UnregisterReporter(nsIMemoryReporter* aReporter)
 {
     // This method is thread-safe.
     mozilla::MutexAutoLock autoLock(mMutex);
 
     if (!mReporters.Contains(aReporter)) {
         return NS_ERROR_FAILURE;
     }
 
     mReporters.RemoveEntry(aReporter);
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsMemoryReporterManager::UnregisterMultiReporter(nsIMemoryMultiReporter* aReporter)
-{
-    // This method is thread-safe.
-    mozilla::MutexAutoLock autoLock(mMutex);
-
-    if (!mMultiReporters.Contains(aReporter)) {
-        return NS_ERROR_FAILURE;
-    }
-
-    mMultiReporters.RemoveEntry(aReporter);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
 nsMemoryReporterManager::BlockRegistration()
 {
     // This method is thread-safe.
     mozilla::MutexAutoLock autoLock(mMutex);
     if (mIsRegistrationBlocked) {
         return NS_ERROR_FAILURE;
     }
     mIsRegistrationBlocked = true;
@@ -991,119 +925,78 @@ nsMemoryReporterManager::GetResident(int
     return ::GetResident(aResident);
 #else
     *aResident = 0;
     return NS_ERROR_NOT_AVAILABLE;
 #endif
 }
 
 // This is just a wrapper for int64_t that implements nsISupports, so it can be
-// passed to nsIMemoryMultiReporter::CollectReports.
+// passed to nsIMemoryReporter::CollectReports.
 class Int64Wrapper MOZ_FINAL : public nsISupports {
 public:
     NS_DECL_ISUPPORTS
     Int64Wrapper() : mValue(0) { }
     int64_t mValue;
 };
 NS_IMPL_ISUPPORTS0(Int64Wrapper)
 
-class ExplicitNonHeapCountingCallback MOZ_FINAL : public nsIMemoryMultiReporterCallback
+class ExplicitCallback MOZ_FINAL : public nsIMemoryReporterCallback
 {
 public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
                         int32_t aKind, int32_t aUnits, int64_t aAmount,
                         const nsACString& aDescription,
-                        nsISupports* aWrappedExplicitNonHeap)
+                        nsISupports* aWrappedExplicit)
     {
-        if (aKind == nsIMemoryReporter::KIND_NONHEAP &&
-            PromiseFlatCString(aPath).Find("explicit") == 0 &&
-            aAmount != int64_t(-1))
+        if (aPath.Equals("heap-allocated") ||
+            (aKind == nsIMemoryReporter::KIND_NONHEAP &&
+             PromiseFlatCString(aPath).Find("explicit") == 0))
         {
-            Int64Wrapper* wrappedPRInt64 =
-                static_cast<Int64Wrapper*>(aWrappedExplicitNonHeap);
-            wrappedPRInt64->mValue += aAmount;
+            Int64Wrapper* wrappedInt64 =
+                static_cast<Int64Wrapper*>(aWrappedExplicit);
+            wrappedInt64->mValue += aAmount;
         }
         return NS_OK;
     }
 };
-NS_IMPL_ISUPPORTS1(
-  ExplicitNonHeapCountingCallback
-, nsIMemoryMultiReporterCallback
-)
+NS_IMPL_ISUPPORTS1(ExplicitCallback, nsIMemoryReporterCallback)
 
 NS_IMETHODIMP
 nsMemoryReporterManager::GetExplicit(int64_t* aExplicit)
 {
     NS_ENSURE_ARG_POINTER(aExplicit);
     *aExplicit = 0;
 #ifndef HAVE_JEMALLOC_STATS
     return NS_ERROR_NOT_AVAILABLE;
 #else
     nsresult rv;
     bool more;
 
-    // Get "heap-allocated" and all the KIND_NONHEAP measurements from normal
-    // (i.e. non-multi) "explicit" reporters.
-    int64_t heapAllocated = int64_t(-1);
-    int64_t explicitNonHeapNormalSize = 0;
+    // For each reporter we call CollectReports and filter out the
+    // non-explicit, non-NONHEAP measurements (except for "heap-allocated").
+    // That's lots of wasted work, and we used to have a GetExplicitNonHeap()
+    // method which did this more efficiently, but it ended up being more
+    // trouble than it was worth.
+
+    nsRefPtr<ExplicitCallback> cb = new ExplicitCallback();
+    nsRefPtr<Int64Wrapper> wrappedExplicitSize = new Int64Wrapper();
+
     nsCOMPtr<nsISimpleEnumerator> e;
     EnumerateReporters(getter_AddRefs(e));
     while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
         nsCOMPtr<nsIMemoryReporter> r;
         e->GetNext(getter_AddRefs(r));
-
-        int32_t kind;
-        rv = r->GetKind(&kind);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        nsCString path;
-        rv = r->GetPath(path);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        // We're only interested in NONHEAP explicit reporters and
-        // the 'heap-allocated' reporter.
-        if (kind == nsIMemoryReporter::KIND_NONHEAP &&
-            path.Find("explicit") == 0)
-        {
-            // Just skip any NONHEAP reporters that fail, because
-            // "heap-allocated" is the most important one.
-            int64_t amount;
-            rv = r->GetAmount(&amount);
-            if (NS_SUCCEEDED(rv)) {
-                explicitNonHeapNormalSize += amount;
-            }
-        } else if (path.Equals("heap-allocated")) {
-            // If we don't have "heap-allocated", give up, because the result
-            // would be horribly inaccurate.
-            rv = r->GetAmount(&heapAllocated);
-            NS_ENSURE_SUCCESS(rv, rv);
-        }
+        r->CollectReports(cb, wrappedExplicitSize);
     }
 
-    // For each multi-reporter we call CollectReports and filter out the
-    // non-explicit, non-NONHEAP measurements.  That's lots of wasted work,
-    // and we used to have a GetExplicitNonHeap() method which did this more
-    // efficiently, but it ended up being more trouble than it was worth.
+    *aExplicit = wrappedExplicitSize->mValue;
 
-    nsRefPtr<ExplicitNonHeapCountingCallback> cb =
-      new ExplicitNonHeapCountingCallback();
-    nsRefPtr<Int64Wrapper> wrappedExplicitNonHeapMultiSize =
-      new Int64Wrapper();
-    nsCOMPtr<nsISimpleEnumerator> e2;
-    EnumerateMultiReporters(getter_AddRefs(e2));
-    while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
-      nsCOMPtr<nsIMemoryMultiReporter> r;
-      e2->GetNext(getter_AddRefs(r));
-      r->CollectReports(cb, wrappedExplicitNonHeapMultiSize);
-    }
-    int64_t explicitNonHeapMultiSize = wrappedExplicitNonHeapMultiSize->mValue;
-
-    *aExplicit = heapAllocated + explicitNonHeapNormalSize + explicitNonHeapMultiSize;
     return NS_OK;
 #endif // HAVE_JEMALLOC_STATS
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::GetHasMozMallocUsableSize(bool* aHas)
 {
     void* p = malloc(16);
@@ -1198,130 +1091,77 @@ nsMemoryReporterManager::MinimizeMemoryU
   NS_ADDREF(*aResult = runnable);
 
   return NS_DispatchToMainThread(runnable);
 }
 
 // Most memory reporters don't need thread safety, but some do.  Make them all
 // thread-safe just to be safe.  Memory reporters are created and destroyed
 // infrequently enough that the performance cost should be negligible.
-NS_IMPL_ISUPPORTS1(MemoryReporterBase, nsIMemoryReporter)
+NS_IMPL_ISUPPORTS1(MemoryUniReporter, nsIMemoryReporter)
 
 nsresult
 NS_RegisterMemoryReporter(nsIMemoryReporter* aReporter)
 {
     nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
     if (!mgr) {
         return NS_ERROR_FAILURE;
     }
     return mgr->RegisterReporter(aReporter);
 }
 
 nsresult
-NS_RegisterMemoryMultiReporter(nsIMemoryMultiReporter* aReporter)
-{
-    nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
-    if (!mgr) {
-        return NS_ERROR_FAILURE;
-    }
-    return mgr->RegisterMultiReporter(aReporter);
-}
-
-nsresult
 NS_UnregisterMemoryReporter(nsIMemoryReporter* aReporter)
 {
     nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
     if (!mgr) {
         return NS_ERROR_FAILURE;
     }
     return mgr->UnregisterReporter(aReporter);
 }
 
-nsresult
-NS_UnregisterMemoryMultiReporter(nsIMemoryMultiReporter* aReporter)
-{
-    nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
-    if (!mgr) {
-        return NS_ERROR_FAILURE;
-    }
-    return mgr->UnregisterMultiReporter(aReporter);
-}
-
 #if defined(MOZ_DMD)
 
 namespace mozilla {
 namespace dmd {
 
-class NullMultiReporterCallback : public nsIMemoryMultiReporterCallback
+class NullReporterCallback : public nsIMemoryReporterCallback
 {
 public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
                         int32_t aKind, int32_t aUnits, int64_t aAmount,
                         const nsACString& aDescription,
                         nsISupports* aData)
     {
         // Do nothing;  the reporter has already reported to DMD.
         return NS_OK;
     }
 };
 NS_IMPL_ISUPPORTS1(
-  NullMultiReporterCallback
-, nsIMemoryMultiReporterCallback
+  NullReporterCallback
+, nsIMemoryReporterCallback
 )
 
 void
 RunReporters()
 {
     nsCOMPtr<nsIMemoryReporterManager> mgr =
         do_GetService("@mozilla.org/memory-reporter-manager;1");
 
-    // Do vanilla reporters.
+    nsRefPtr<NullReporterCallback> cb = new NullReporterCallback();
+
+    bool more;
     nsCOMPtr<nsISimpleEnumerator> e;
     mgr->EnumerateReporters(getter_AddRefs(e));
-    bool more;
     while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
         nsCOMPtr<nsIMemoryReporter> r;
         e->GetNext(getter_AddRefs(r));
-
-        int32_t kind;
-        nsresult rv = r->GetKind(&kind);
-        if (NS_FAILED(rv)) {
-            continue;
-        }
-        nsCString path;
-        rv = r->GetPath(path);
-        if (NS_FAILED(rv)) {
-            continue;
-        }
-
-        // We're only interested in HEAP explicit reporters.  (In particular,
-        // some heap blocks are deliberately measured once inside an "explicit"
-        // reporter and once outside, which isn't a problem.  This condition
-        // prevents them being reported as double-counted.  See bug 811018
-        // comment 2.)
-        if (kind == nsIMemoryReporter::KIND_HEAP &&
-            path.Find("explicit") == 0)
-        {
-            // Just getting the amount is enough for the reporter to report to
-            // DMD.
-            int64_t amount;
-            (void)r->GetAmount(&amount);
-        }
-    }
-
-    // Do multi-reporters.
-    nsCOMPtr<nsISimpleEnumerator> e2;
-    mgr->EnumerateMultiReporters(getter_AddRefs(e2));
-    nsRefPtr<NullMultiReporterCallback> cb = new NullMultiReporterCallback();
-    while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) {
-      nsCOMPtr<nsIMemoryMultiReporter> r;
-      e2->GetNext(getter_AddRefs(r));
-      r->CollectReports(cb, nullptr);
+        r->CollectReports(cb, nullptr);
     }
 }
 
 } // namespace dmd
 } // namespace mozilla
 
 #endif  // defined(MOZ_DMD)
 
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -19,20 +19,17 @@ public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTERMANAGER
 
   nsMemoryReporterManager();
   virtual ~nsMemoryReporterManager();
 
 private:
   nsresult RegisterReporterHelper(nsIMemoryReporter *reporter, bool aForce);
-  nsresult RegisterMultiReporterHelper(nsIMemoryMultiReporter *reporter,
-                                       bool aForce);
 
   nsTHashtable<nsISupportsHashKey> mReporters;
-  nsTHashtable<nsISupportsHashKey> mMultiReporters;
   Mutex mMutex;
   bool mIsRegistrationBlocked;
 };
 
 #define NS_MEMORY_REPORTER_MANAGER_CID \
 { 0xfb97e4f5, 0x32dd, 0x497a, \
 { 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } }
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -327,21 +327,21 @@ NS_GetTraceRefcnt(nsITraceRefcnt** resul
 
 EXPORT_XPCOM_API(nsresult)
 NS_InitXPCOM(nsIServiceManager* *result,
                              nsIFile* binDirectory)
 {
     return NS_InitXPCOM2(result, binDirectory, nullptr);
 }
 
-class ICUReporter MOZ_FINAL : public MemoryReporterBase
+class ICUReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     ICUReporter()
-      : MemoryReporterBase("explicit/icu", KIND_HEAP, UNITS_BYTES,
+      : MemoryUniReporter("explicit/icu", KIND_HEAP, UNITS_BYTES,
 "Memory used by ICU, a Unicode and globalization support library.")
     {
 #ifdef DEBUG
         // There must be only one instance of this class, due to |sAmount|
         // being static.
         static bool hasRun = false;
         MOZ_ASSERT(!hasRun);
         hasRun = true;
--- a/xpcom/components/nsCategoryManager.cpp
+++ b/xpcom/components/nsCategoryManager.cpp
@@ -396,21 +396,21 @@ CategoryEnumerator::enumfunc_createenume
 
 
 //
 // nsCategoryManager implementations
 //
 
 NS_IMPL_QUERY_INTERFACE1(nsCategoryManager, nsICategoryManager)
 
-class XPCOMCategoryManagerReporter MOZ_FINAL : public MemoryReporterBase
+class XPCOMCategoryManagerReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     XPCOMCategoryManagerReporter()
-      : MemoryReporterBase("explicit/xpcom/category-manager",
+      : MemoryUniReporter("explicit/xpcom/category-manager",
                            KIND_HEAP, UNITS_BYTES,
                            "Memory used for the XPCOM category manager.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
         return nsCategoryManager::SizeOfIncludingThis(MallocSizeOf);
     }
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -275,21 +275,21 @@ CloneAndAppend(nsIFile* aBase, const nsA
     f->AppendNative(append);
     return f.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsComponentManagerImpl
 ////////////////////////////////////////////////////////////////////////////////
 
-class XPCOMComponentManagerReporter MOZ_FINAL : public MemoryReporterBase
+class XPCOMComponentManagerReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     XPCOMComponentManagerReporter()
-      : MemoryReporterBase("explicit/xpcom/component-manager",
+      : MemoryUniReporter("explicit/xpcom/component-manager",
                            KIND_HEAP, UNITS_BYTES,
                            "Memory used for the XPCOM component manager.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
         return nsComponentManagerImpl::gComponentManager
              ? nsComponentManagerImpl::gComponentManager->SizeOfIncludingThis(
--- a/xpcom/ds/nsObserverService.cpp
+++ b/xpcom/ds/nsObserverService.cpp
@@ -43,28 +43,28 @@ GetObserverServiceLog()
 }
   #define LOG(x)  PR_LOG(GetObserverServiceLog(), PR_LOG_DEBUG, x)
 #else
   #define LOG(x)
 #endif /* PR_LOGGING */
 
 namespace mozilla {
 
-class ObserverServiceReporter MOZ_FINAL : public nsIMemoryMultiReporter
+class ObserverServiceReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
     NS_DECL_ISUPPORTS
-    NS_DECL_NSIMEMORYMULTIREPORTER
+    NS_DECL_NSIMEMORYREPORTER
 protected:
     static const size_t kSuspectReferentCount = 1000;
     static PLDHashOperator CountReferents(nsObserverList* aObserverList,
                                           void* aClosure);
 };
 
-NS_IMPL_ISUPPORTS1(ObserverServiceReporter, nsIMemoryMultiReporter)
+NS_IMPL_ISUPPORTS1(ObserverServiceReporter, nsIMemoryReporter)
 
 NS_IMETHODIMP
 ObserverServiceReporter::GetName(nsACString& aName)
 {
     aName.AssignLiteral("observer-service");
     return NS_OK;
 }
 
@@ -123,17 +123,17 @@ ObserverServiceReporter::CountReferents(
         SuspectObserver suspect(aObserverList->GetKey(), total);
         referentCount->suspectObservers.AppendElement(suspect);
     }
 
     return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
-ObserverServiceReporter::CollectReports(nsIMemoryMultiReporterCallback* cb,
+ObserverServiceReporter::CollectReports(nsIMemoryReporterCallback* cb,
                                         nsISupports* aClosure)
 {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     nsObserverService* service = static_cast<nsObserverService*>(os.get());
     if (!service) {
         return NS_OK;
     }
 
@@ -214,24 +214,24 @@ nsObserverService::~nsObserverService(vo
 {
     Shutdown();
 }
 
 void
 nsObserverService::RegisterReporter()
 {
     mReporter = new ObserverServiceReporter();
-    NS_RegisterMemoryMultiReporter(mReporter);
+    NS_RegisterMemoryReporter(mReporter);
 }
 
 void
 nsObserverService::Shutdown()
 {
     if (mReporter) {
-        NS_UnregisterMemoryMultiReporter(mReporter);
+        NS_UnregisterMemoryReporter(mReporter);
     }
 
     mShuttingDown = true;
 
     mObserverTopicTable.Clear();
 }
 
 nsresult
--- a/xpcom/ds/nsObserverService.h
+++ b/xpcom/ds/nsObserverService.h
@@ -10,17 +10,17 @@
 #include "nsObserverList.h"
 #include "nsTHashtable.h"
 #include "mozilla/Attributes.h"
 
 // {D07F5195-E3D1-11d2-8ACD-00105A1B8860}
 #define NS_OBSERVERSERVICE_CID \
     { 0xd07f5195, 0xe3d1, 0x11d2, { 0x8a, 0xcd, 0x0, 0x10, 0x5a, 0x1b, 0x88, 0x60 } }
 
-class nsIMemoryMultiReporter;
+class nsIMemoryReporter;
 
 namespace mozilla {
 class ObserverServiceReporter;
 } // namespace mozilla
 
 class nsObserverService MOZ_FINAL : public nsIObserverService {
   friend class mozilla::ObserverServiceReporter;
 
@@ -42,14 +42,14 @@ public:
   NS_IMETHOD UnmarkGrayStrongObservers();
 
 private:
   ~nsObserverService(void);
   void RegisterReporter();
 
   bool mShuttingDown;
   nsTHashtable<nsObserverList> mObserverTopicTable;
-  nsCOMPtr<nsIMemoryMultiReporter> mReporter;
+  nsCOMPtr<nsIMemoryReporter> mReporter;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsObserverService, NS_OBSERVERSERVICE_CID)
 
 #endif /* nsObserverService_h___ */
--- a/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
+++ b/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
@@ -35,21 +35,21 @@ XPTInterfaceInfoManager::SizeOfIncluding
     ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
     // The entries themselves are allocated out of an arena accounted
     // for elsewhere, so don't measure them
     n += mWorkingSet.mIIDTable.SizeOfExcludingThis(NULL, aMallocSizeOf);
     n += mWorkingSet.mNameTable.SizeOfExcludingThis(NULL, aMallocSizeOf);
     return n;
 }
 
-class XPTIWorkingSetReporter MOZ_FINAL : public MemoryReporterBase
+class XPTIWorkingSetReporter MOZ_FINAL : public MemoryUniReporter
 {
 public:
     XPTIWorkingSetReporter()
-      : MemoryReporterBase("explicit/xpti-working-set", KIND_HEAP, UNITS_BYTES,
+      : MemoryUniReporter("explicit/xpti-working-set", KIND_HEAP, UNITS_BYTES,
                            "Memory used by the XPCOM typelib system.")
     {}
 private:
     int64_t Amount() MOZ_OVERRIDE
     {
         size_t n = gInterfaceInfoManager
                  ? gInterfaceInfoManager->SizeOfIncludingThis(MallocSizeOf)
                  : 0;