Bug 1010064 - Allow memory reports to be anonymized. r=bsmedberg.
authorNicholas Nethercote <nnethercote@mozilla.com>
Tue, 20 May 2014 23:06:54 -0700
changeset 199216 258916327d96add07d88990a8e13df3fd080f2d2
parent 199215 8eeb287c59fd0b7a8b3a0b66161b52c5833ac523
child 199217 87e3c9420691b0e27b7b5740b9650548dfce25cb
push idunknown
push userunknown
push dateunknown
reviewersbsmedberg
bugs1010064
milestone33.0a1
Bug 1010064 - Allow memory reports to be anonymized. r=bsmedberg.
addon-sdk/source/lib/sdk/test/harness.js
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMFile.cpp
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsHostObjectProtocolHandler.cpp
content/canvas/src/CanvasRenderingContext2D.cpp
content/canvas/src/WebGLContextReporter.cpp
content/media/MediaDecoder.cpp
content/media/MediaStreamGraph.cpp
content/media/webaudio/AudioContext.cpp
content/media/webaudio/AudioContext.h
dom/base/nsScriptNameSpaceManager.cpp
dom/base/nsWindowMemoryReporter.cpp
dom/base/nsWindowMemoryReporter.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/settings/SettingsManager.js
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
extensions/spellcheck/hunspell/src/mozHunspell.h
gfx/gl/GfxTexturesReporter.h
gfx/layers/ipc/ISurfaceAllocator.h
gfx/layers/ipc/SharedBufferManagerParent.cpp
gfx/thebes/gfxASurface.cpp
gfx/thebes/gfxAndroidPlatform.cpp
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxWindowsPlatform.cpp
image/src/SurfaceCache.cpp
image/src/imgLoader.cpp
image/test/mochitest/test_bug601470.html
ipc/glue/SharedMemory.cpp
js/public/MemoryMetrics.h
js/src/vm/MemoryMetrics.cpp
js/xpconnect/src/XPCJSMemoryReporter.h
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/xpcpublic.h
layout/base/nsStyleSheetService.cpp
layout/style/nsLayoutStylesheetCache.cpp
mobile/android/chrome/content/MemoryObserver.js
modules/libpref/src/Preferences.cpp
netwerk/cache/nsCacheService.cpp
netwerk/cache2/CacheStorageService.cpp
netwerk/cookie/nsCookieService.cpp
netwerk/dns/nsDNSService2.cpp
netwerk/dns/nsEffectiveTLDService.cpp
netwerk/protocol/http/SpdyZlibReporter.cpp
netwerk/protocol/http/SpdyZlibReporter.h
startupcache/StartupCache.cpp
storage/src/mozStorageService.cpp
testing/mochitest/tests/SimpleTest/MemoryStats.js
toolkit/components/aboutmemory/content/aboutMemory.js
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_aboutmemory5.xul
toolkit/components/aboutmemory/tests/test_memoryReporters.xul
toolkit/components/aboutmemory/tests/test_memoryReporters2.xul
toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul
toolkit/components/places/History.cpp
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
xpcom/base/AvailableMemoryTracker.cpp
xpcom/base/SystemMemoryReporter.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsIMemoryInfoDumper.idl
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/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
--- a/addon-sdk/source/lib/sdk/test/harness.js
+++ b/addon-sdk/source/lib/sdk/test/harness.js
@@ -156,17 +156,17 @@ function reportMemoryUsage() {
 
   return gcPromise().then((function () {
     var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
               .getService(Ci.nsIMemoryReporterManager);
     let count = 0;
     function logReporter(process, path, kind, units, amount, description) {
       print(((++count == 1) ? "\n" : "") + description + ": " + amount + "\n");
     }
-    mgr.getReportsForThisProcess(logReporter, null);
+    mgr.getReportsForThisProcess(logReporter, null, /* anonymize = */ false);
 
     var weakrefs = [info.weakref.get()
                     for each (info in memory.getObjects())];
     weakrefs = [weakref for each (weakref in weakrefs) if (weakref)];
     print("Tracked memory objects in testing sandbox: " + weakrefs.length + "\n");
   }));
 }
 
@@ -375,17 +375,17 @@ function getPotentialLeaks() {
         return;
 
       windows[matches[1]] = item;
     }
   }
 
   Cc["@mozilla.org/memory-reporter-manager;1"]
     .getService(Ci.nsIMemoryReporterManager)
-    .getReportsForThisProcess(logReporter, null);
+    .getReportsForThisProcess(logReporter, null, /* anonymize = */ false);
 
   return { compartments: compartments, windows: windows };
 }
 
 function nextIteration(tests) {
   if (tests) {
     results.passed += tests.passed;
     results.failed += tests.failed;
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -415,17 +415,17 @@ static PLDHashTable sEventListenerManage
 class DOMEventListenerManagersHashReporter MOZ_FINAL : public nsIMemoryReporter
 {
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData)
+                            nsISupports* aData, bool aAnonymize)
   {
     // We don't measure the |EventListenerManager| objects pointed to by the
     // entries because those references are non-owning.
     int64_t amount = sEventListenerManagersHash.ops
                    ? PL_DHashTableSizeOfExcludingThis(
                        &sEventListenerManagersHash, nullptr, MallocSizeOf)
                    : 0;
 
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -637,17 +637,17 @@ MOZ_DEFINE_MALLOC_SIZE_OF(DOMMemoryFileD
 
 class nsDOMMemoryFileDataOwnerMemoryReporter MOZ_FINAL
   : public nsIMemoryReporter
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCallback,
-                            nsISupports *aClosure)
+                            nsISupports *aClosure, bool aAnonymize)
   {
     typedef nsDOMMemoryFile::DataOwner DataOwner;
 
     StaticMutexAutoLock lock(DataOwner::sDataOwnerMutex);
 
     if (!DataOwner::sDataOwners) {
       return NS_OK;
     }
@@ -657,33 +657,32 @@ public:
 
     for (DataOwner *owner = DataOwner::sDataOwners->getFirst();
          owner; owner = owner->getNext()) {
 
       size_t size = DOMMemoryFileDataOwnerMallocSizeOf(owner->mData);
 
       if (size < LARGE_OBJECT_MIN_SIZE) {
         smallObjectsTotal += size;
-      }
-      else {
+      } else {
         SHA1Sum sha1;
         sha1.update(owner->mData, owner->mLength);
         uint8_t digest[SHA1Sum::HashSize]; // SHA1 digests are 20 bytes long.
         sha1.finish(digest);
 
         nsAutoCString digestString;
         for (size_t i = 0; i < sizeof(digest); i++) {
           digestString.AppendPrintf("%02x", digest[i]);
         }
 
         nsresult rv = aCallback->Callback(
           /* process */ NS_LITERAL_CSTRING(""),
           nsPrintfCString(
             "explicit/dom/memory-file-data/large/file(length=%llu, sha1=%s)",
-            owner->mLength, digestString.get()),
+            owner->mLength, aAnonymize ? "<anonymized>" : digestString.get()),
           KIND_HEAP, UNITS_BYTES, size,
           nsPrintfCString(
             "Memory used to back a memory file of length %llu bytes.  The file "
             "has a sha1 of %s.\n\n"
             "Note that the allocator may round up a memory file's length -- "
             "that is, an N-byte memory file may take up more than N bytes of "
             "memory.",
             owner->mLength, digestString.get()),
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -1325,17 +1325,17 @@ ReportReferentCount(const char* aManager
 
 #undef REPORT
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MessageManagerReporter::CollectReports(nsIMemoryReporterCallback* aCb,
-                                       nsISupports* aClosure)
+                                       nsISupports* aClosure, bool aAnonymize)
 {
   nsresult rv;
 
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     nsCOMPtr<nsIMessageBroadcaster> globalmm =
       do_GetService("@mozilla.org/globalmessagemanager;1");
     if (globalmm) {
       nsRefPtr<nsFrameMessageManager> mm =
--- a/content/base/src/nsHostObjectProtocolHandler.cpp
+++ b/content/base/src/nsHostObjectProtocolHandler.cpp
@@ -32,17 +32,17 @@ static nsClassHashtable<nsCStringHashKey
 namespace mozilla {
 
 class HostObjectURLsReporter MOZ_FINAL : public nsIMemoryReporter
 {
  public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData)
+                            nsISupports* aData, bool aAnonymize)
   {
     return MOZ_COLLECT_REPORT(
       "host-object-urls", KIND_OTHER, UNITS_COUNT,
       gDataTable ? gDataTable->Count() : 0,
       "The number of host objects stored for access via URLs "
       "(e.g. blobs passed to URL.createObjectURL).");
   }
 };
@@ -50,21 +50,22 @@ class HostObjectURLsReporter MOZ_FINAL :
 NS_IMPL_ISUPPORTS(HostObjectURLsReporter, nsIMemoryReporter)
 
 class BlobURLsReporter MOZ_FINAL : public nsIMemoryReporter
 {
  public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aCallback,
-                            nsISupports* aData)
+                            nsISupports* aData, bool aAnonymize)
   {
     EnumArg env;
     env.mCallback = aCallback;
     env.mData = aData;
+    env.mAnonymize = aAnonymize;
 
     if (gDataTable) {
       gDataTable->EnumerateRead(CountCallback, &env);
       gDataTable->EnumerateRead(ReportCallback, &env);
     }
     return NS_OK;
   }
 
@@ -129,16 +130,17 @@ class BlobURLsReporter MOZ_FINAL : publi
       NS_ENSURE_SUCCESS_VOID(rv);
     }
   }
 
  private:
   struct EnumArg {
     nsIHandleReportCallback* mCallback;
     nsISupports* mData;
+    bool mAnonymize;
     nsDataHashtable<nsPtrHashKey<nsIDOMBlob>, uint32_t> mRefCounts;
   };
 
   // Determine number of URLs per blob, to handle the case where it's > 1.
   static PLDHashOperator CountCallback(nsCStringHashKey::KeyType aKey,
                                        DataInfo* aInfo,
                                        void* aUserArg)
   {
@@ -185,26 +187,38 @@ class BlobURLsReporter MOZ_FINAL : publi
 
       path = isMemoryFile ? "memory-blob-urls/" : "file-blob-urls/";
       if (NS_SUCCEEDED(aInfo->mPrincipal->GetURI(getter_AddRefs(principalURI))) &&
           principalURI != nullptr &&
           NS_SUCCEEDED(principalURI->GetSpec(owner)) &&
           !owner.IsEmpty()) {
         owner.ReplaceChar('/', '\\');
         path += "owner(";
-        path += owner;
+        if (envp->mAnonymize) {
+          path += "<anonymized>";
+        } else {
+          path += owner;
+        }
         path += ")";
       } else {
         path += "owner unknown";
       }
       path += "/";
-      path += aInfo->mStack;
+      if (envp->mAnonymize) {
+        path += "<anonymized-stack>";
+      } else {
+        path += aInfo->mStack;
+      }
       url = aKey;
       url.ReplaceChar('/', '\\');
-      path += url;
+      if (envp->mAnonymize) {
+        path += "<anonymized-url>";
+      } else {
+        path += url;
+      }
       if (refCount > 1) {
         nsAutoCString addrStr;
 
         addrStr = "0x";
         addrStr.AppendInt((uint64_t)(nsIDOMBlob*)blob, 16);
 
         path += " ";
         path.AppendInt(refCount);
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -145,17 +145,17 @@ static int64_t gCanvasAzureMemoryUsed = 
 // 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 nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData)
+                            nsISupports* aData, bool aAnonymize)
   {
     return MOZ_COLLECT_REPORT(
       "canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
       gCanvasAzureMemoryUsed,
       "Memory used by 2D canvases. Each canvas requires "
       "(width * height * 4) bytes.");
   }
 };
--- a/content/canvas/src/WebGLContextReporter.cpp
+++ b/content/canvas/src/WebGLContextReporter.cpp
@@ -7,17 +7,17 @@
 #include "WebGLMemoryTracker.h"
 
 using namespace mozilla;
 
 NS_IMPL_ISUPPORTS(WebGLMemoryPressureObserver, nsIObserver)
 
 NS_IMETHODIMP
 WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                   nsISupports* aData)
+                                   nsISupports* aData, bool aAnonymize)
 {
 #define REPORT(_path, _kind, _units, _amount, _desc)                          \
     do {                                                                      \
       nsresult rv;                                                            \
       rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
                                    _kind, _units, _amount,                    \
                                    NS_LITERAL_CSTRING(_desc), aData);         \
       NS_ENSURE_SUCCESS(rv, rv);                                              \
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -1738,17 +1738,17 @@ bool
 MediaDecoder::IsAppleMP3Enabled()
 {
   return Preferences::GetBool("media.apple.mp3.enabled");
 }
 #endif
 
 NS_IMETHODIMP
 MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                   nsISupports* aData)
+                                   nsISupports* aData, bool aAnonymize)
 {
   int64_t video = 0, audio = 0;
   size_t resources = 0;
   DecodersArray& decoders = Decoders();
   for (size_t i = 0; i < decoders.Length(); ++i) {
     MediaDecoder* decoder = decoders[i];
     video += decoder->SizeOfVideoQueue();
     audio += decoder->SizeOfAudioQueue();
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -2746,17 +2746,17 @@ struct ArrayClearer
 {
   ArrayClearer(nsTArray<AudioNodeSizes>& aArray) : mArray(aArray) {}
   ~ArrayClearer() { mArray.Clear(); }
   nsTArray<AudioNodeSizes>& mArray;
 };
 
 NS_IMETHODIMP
 MediaStreamGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                     nsISupports* aData)
+                                     nsISupports* aData, bool aAnonymize)
 {
   // Clears out the report array after we're done with it.
   ArrayClearer reportCleanup(mAudioStreamSizes);
 
   {
     MonitorAutoLock memoryReportLock(mMemoryReportMonitor);
     mNeedsMemoryReport = true;
 
@@ -2784,27 +2784,27 @@ MediaStreamGraphImpl::CollectReports(nsI
     NS_ENSURE_SUCCESS(rv, rv);                                              \
   } while (0)
 
   for (size_t i = 0; i < mAudioStreamSizes.Length(); i++) {
     const AudioNodeSizes& usage = mAudioStreamSizes[i];
     const char* const nodeType =  usage.mNodeType.get();
 
     nsPrintfCString domNodePath("explicit/webaudio/audio-node/%s/dom-nodes",
-                                  nodeType);
+                                nodeType);
     REPORT(domNodePath, usage.mDomNode,
            "Memory used by AudioNode DOM objects (Web Audio).");
 
     nsPrintfCString enginePath("explicit/webaudio/audio-node/%s/engine-objects",
-                                nodeType);
+                               nodeType);
     REPORT(enginePath, usage.mEngine,
            "Memory used by AudioNode engine objects (Web Audio).");
 
     nsPrintfCString streamPath("explicit/webaudio/audio-node/%s/stream-objects",
-                                nodeType);
+                               nodeType);
     REPORT(streamPath, usage.mStream,
            "Memory used by AudioNode stream objects (Web Audio).");
 
   }
 
 #undef REPORT
 
   return NS_OK;
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -670,17 +670,17 @@ AudioContext::SizeOfIncludingThis(mozill
   }
   amount += mActiveNodes.SizeOfExcludingThis(nullptr, aMallocSizeOf);
   amount += mPannerNodes.SizeOfExcludingThis(nullptr, aMallocSizeOf);
   return amount;
 }
 
 NS_IMETHODIMP
 AudioContext::CollectReports(nsIHandleReportCallback* aHandleReport,
-                             nsISupports* aData)
+                             nsISupports* aData, bool aAnonymize)
 {
   int64_t amount = SizeOfIncludingThis(MallocSizeOf);
   return MOZ_COLLECT_REPORT("explicit/webaudio/audiocontext", KIND_HEAP, UNITS_BYTES,
                             amount, "Memory used by AudioContext objects (Web Audio).");
 }
 
 double
 AudioContext::ExtraCurrentTime() const
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -244,17 +244,17 @@ private:
    */
   double ExtraCurrentTime() const;
 
   void RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob);
   void ShutdownDecoder();
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData);
+                            nsISupports* aData, bool aAnonymize);
 
   friend struct ::mozilla::WebAudioDecodeJob;
 
 private:
   // Note that it's important for mSampleRate to be initialized before
   // mDestination, as mDestination's constructor needs to access it!
   const float mSampleRate;
   nsRefPtr<AudioDestinationNode> mDestination;
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -811,17 +811,17 @@ SizeOfEntryExcludingThis(PLDHashEntryHdr
   GlobalNameMapEntry* entry = static_cast<GlobalNameMapEntry*>(aHdr);
   return entry->SizeOfExcludingThis(aMallocSizeOf);
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(ScriptNameSpaceManagerMallocSizeOf)
 
 NS_IMETHODIMP
 nsScriptNameSpaceManager::CollectReports(
-  nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+  nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
 {
   return MOZ_COLLECT_REPORT(
     "explicit/script-namespace-manager", KIND_HEAP, UNITS_BYTES,
     SizeOfIncludingThis(ScriptNameSpaceManagerMallocSizeOf),
     "Memory used for the script namespace manager.");
 }
 
 size_t
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -157,30 +157,34 @@ GetWindowURI(nsIDOMWindow *aWindow)
       }
     }
   }
 
   return uri.forget();
 }
 
 static void
-AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr)
+AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr, bool aAnonymize)
 {
   nsCOMPtr<nsIURI> uri = GetWindowURI(aWindow);
 
   if (uri) {
-    nsCString spec;
-    uri->GetSpec(spec);
+    if (aAnonymize && !aWindow->IsChromeWindow()) {
+      aStr.AppendPrintf("<anonymized-%d>", aWindow->WindowID());
+    } else {
+      nsCString spec;
+      uri->GetSpec(spec);
 
-    // A hack: replace forward slashes with '\\' so they aren't
-    // treated as path separators.  Users of the reporters
-    // (such as about:memory) have to undo this change.
-    spec.ReplaceChar('/', '\\');
+      // A hack: replace forward slashes with '\\' so they aren't
+      // treated as path separators.  Users of the reporters
+      // (such as about:memory) have to undo this change.
+      spec.ReplaceChar('/', '\\');
 
-    aStr += spec;
+      aStr += spec;
+    }
   } else {
     // If we're unable to find a URI, we're dealing with a chrome window with
     // no document in it (or somesuch), so we call this a "system window".
     aStr += NS_LITERAL_CSTRING("[system]");
   }
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(WindowsMallocSizeOf)
@@ -231,17 +235,18 @@ ReportCount(const nsCString& aBasePath, 
 static nsresult
 CollectWindowReports(nsGlobalWindow *aWindow,
                      amIAddonManager *addonManager,
                      nsWindowSizes *aWindowTotalSizes,
                      nsTHashtable<nsUint64HashKey> *aGhostWindowIDs,
                      WindowPaths *aWindowPaths,
                      WindowPaths *aTopWindowPaths,
                      nsIMemoryReporterCallback *aCb,
-                     nsISupports *aClosure)
+                     nsISupports *aClosure,
+                     bool aAnonymize)
 {
   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 = nullptr;
   nsCOMPtr<nsIURI> location;
   if (aWindow->GetOuterWindow()) {
@@ -255,26 +260,28 @@ CollectWindowReports(nsGlobalWindow *aWi
   if (!location) {
     location = GetWindowURI(aWindow);
   }
 
   if (addonManager && location) {
     bool ok;
     nsAutoCString id;
     if (NS_SUCCEEDED(addonManager->MapURIToAddonID(location, id, &ok)) && ok) {
+      // Add-on names are not privacy-sensitive, so we can use them with
+      // impunity.
       windowPath += NS_LITERAL_CSTRING("add-ons/") + id +
                     NS_LITERAL_CSTRING("/");
     }
   }
 
   windowPath += NS_LITERAL_CSTRING("window-objects/");
 
   if (top) {
     windowPath += NS_LITERAL_CSTRING("top(");
-    AppendWindowURI(top, windowPath);
+    AppendWindowURI(top, windowPath, aAnonymize);
     windowPath += NS_LITERAL_CSTRING(", id=");
     windowPath.AppendInt(top->WindowID());
     windowPath += NS_LITERAL_CSTRING(")");
 
     aTopWindowPaths->Put(aWindow->WindowID(), windowPath);
 
     windowPath += aWindow->IsFrozen() ? NS_LITERAL_CSTRING("/cached/")
                                       : NS_LITERAL_CSTRING("/active/");
@@ -282,17 +289,17 @@ CollectWindowReports(nsGlobalWindow *aWi
     if (aGhostWindowIDs->Contains(aWindow->WindowID())) {
       windowPath += NS_LITERAL_CSTRING("top(none)/ghost/");
     } else {
       windowPath += NS_LITERAL_CSTRING("top(none)/detached/");
     }
   }
 
   windowPath += NS_LITERAL_CSTRING("window(");
-  AppendWindowURI(aWindow, windowPath);
+  AppendWindowURI(aWindow, windowPath, aAnonymize);
   windowPath += NS_LITERAL_CSTRING(")");
 
   // Use |windowPath|, but replace "explicit/" with "event-counts/".
   nsCString censusWindowPath(windowPath);
   censusWindowPath.Replace(0, strlen("explicit"), "event-counts");
 
   // Remember the path for later.
   aWindowPaths->Put(aWindow->WindowID(), windowPath);
@@ -441,106 +448,107 @@ GetWindows(const uint64_t& aId, nsGlobal
 {
   ((WindowArray *)aClosure)->AppendElement(aWindow);
 
   return PL_DHASH_NEXT;
 }
 
 struct ReportGhostWindowsEnumeratorData
 {
-  nsIMemoryReporterCallback* callback;
-  nsISupports* closure;
-  nsresult rv;
+  nsIMemoryReporterCallback* mCallback;
+  nsISupports* mData;
+  bool mAnonymize;
+  nsresult mRv;
 };
 
 static PLDHashOperator
-ReportGhostWindowsEnumerator(nsUint64HashKey* aIDHashKey, void* aClosure)
+ReportGhostWindowsEnumerator(nsUint64HashKey* aIDHashKey, void* aData)
 {
   ReportGhostWindowsEnumeratorData *data =
-    static_cast<ReportGhostWindowsEnumeratorData*>(aClosure);
+    static_cast<ReportGhostWindowsEnumeratorData*>(aData);
 
   nsGlobalWindow::WindowByIdTable* windowsById =
     nsGlobalWindow::GetWindowsTable();
   if (!windowsById) {
     NS_WARNING("Couldn't get window-by-id hashtable?");
     return PL_DHASH_NEXT;
   }
 
   nsGlobalWindow* window = windowsById->Get(aIDHashKey->GetKey());
   if (!window) {
     NS_WARNING("Could not look up window?");
     return PL_DHASH_NEXT;
   }
 
   nsAutoCString path;
   path.AppendLiteral("ghost-windows/");
-  AppendWindowURI(window, path);
+  AppendWindowURI(window, path, data->mAnonymize);
 
-  nsresult rv = data->callback->Callback(
+  nsresult rv = data->mCallback->Callback(
     /* process = */ EmptyCString(),
     path,
     nsIMemoryReporter::KIND_OTHER,
     nsIMemoryReporter::UNITS_COUNT,
     /* amount = */ 1,
     /* description = */ NS_LITERAL_CSTRING("A ghost window."),
-    data->closure);
+    data->mData);
 
-  if (NS_FAILED(rv) && NS_SUCCEEDED(data->rv)) {
-    data->rv = rv;
+  if (NS_FAILED(rv) && NS_SUCCEEDED(data->mRv)) {
+    data->mRv = rv;
   }
 
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 nsWindowMemoryReporter::CollectReports(nsIMemoryReporterCallback* aCb,
-                                       nsISupports* aClosure)
+                                       nsISupports* aClosure, bool aAnonymize)
 {
   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.
   WindowArray windows;
   windowsById->Enumerate(GetWindows, &windows);
 
   // Get the IDs of all the "ghost" windows, and call aCb->Callback() for each
   // one.
   nsTHashtable<nsUint64HashKey> ghostWindows;
   CheckForGhostWindows(&ghostWindows);
   ReportGhostWindowsEnumeratorData reportGhostWindowsEnumData =
-    { aCb, aClosure, NS_OK };
+    { aCb, aClosure, aAnonymize, NS_OK };
   ghostWindows.EnumerateEntries(ReportGhostWindowsEnumerator,
                                 &reportGhostWindowsEnumData);
-  nsresult rv = reportGhostWindowsEnumData.rv;
+  nsresult rv = reportGhostWindowsEnumData.mRv;
   NS_ENSURE_SUCCESS(rv, rv);
 
   WindowPaths windowPaths;
   WindowPaths topWindowPaths;
 
   // Collect window memory usage.
   nsWindowSizes windowTotalSizes(nullptr);
   nsCOMPtr<amIAddonManager> addonManager;
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     // Only try to access the service from the main process.
     addonManager = do_GetService("@mozilla.org/addons/integration;1");
   }
   for (uint32_t i = 0; i < windows.Length(); i++) {
     rv = CollectWindowReports(windows[i], addonManager,
                               &windowTotalSizes, &ghostWindows,
                               &windowPaths, &topWindowPaths, aCb,
-                              aClosure);
+                              aClosure, aAnonymize);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Report JS memory usage.  We do this from here because the JS memory
   // reporter needs to be passed |windowPaths|.
   rv = xpc::JSReporter::CollectReports(&windowPaths, &topWindowPaths,
-                                       aCb, aClosure);
+                                       aCb, aClosure, aAnonymize);
   NS_ENSURE_SUCCESS(rv, rv);
 
 #define REPORT(_path, _amount, _desc)                                         \
   do {                                                                        \
     nsresult rv;                                                              \
     rv = aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path),             \
                        KIND_OTHER, UNITS_BYTES, _amount,                      \
                        NS_LITERAL_CSTRING(_desc), aClosure);                  \
--- a/dom/base/nsWindowMemoryReporter.h
+++ b/dom/base/nsWindowMemoryReporter.h
@@ -168,17 +168,18 @@ private:
   class GhostWindowsReporter MOZ_FINAL : public nsIMemoryReporter
   {
   public:
     NS_DECL_ISUPPORTS
 
     static int64_t DistinguishedAmount();
 
     NS_IMETHOD
-    CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+    CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+                   bool aAnonymize)
     {
       return MOZ_COLLECT_REPORT(
         "ghost-windows", KIND_OTHER, UNITS_COUNT, DistinguishedAmount(),
 "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 "
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -185,28 +185,32 @@ namespace mozilla {
 namespace dom {
 
 class MemoryReportRequestChild : public PMemoryReportRequestChild,
                                  public nsIRunnable
 {
 public:
     NS_DECL_ISUPPORTS
 
-    MemoryReportRequestChild(uint32_t aGeneration, const nsAString& aDMDDumpIdent);
+    MemoryReportRequestChild(uint32_t aGeneration, bool aAnonymize,
+                             const nsAString& aDMDDumpIdent);
     virtual ~MemoryReportRequestChild();
     NS_IMETHOD Run();
 private:
     uint32_t mGeneration;
+    bool     mAnonymize;
     nsString mDMDDumpIdent;
 };
 
 NS_IMPL_ISUPPORTS(MemoryReportRequestChild, nsIRunnable)
 
-MemoryReportRequestChild::MemoryReportRequestChild(uint32_t aGeneration, const nsAString& aDMDDumpIdent)
-: mGeneration(aGeneration), mDMDDumpIdent(aDMDDumpIdent)
+MemoryReportRequestChild::MemoryReportRequestChild(
+    uint32_t aGeneration, bool aAnonymize, const nsAString& aDMDDumpIdent)
+  : mGeneration(aGeneration), mAnonymize(aAnonymize),
+    mDMDDumpIdent(aDMDDumpIdent)
 {
     MOZ_COUNT_CTOR(MemoryReportRequestChild);
 }
 
 MemoryReportRequestChild::~MemoryReportRequestChild()
 {
     MOZ_COUNT_DTOR(MemoryReportRequestChild);
 }
@@ -667,21 +671,23 @@ ContentChild::InitXPCOM()
     sysMsgObserver->Init();
 
 #ifndef MOZ_NUWA_PROCESS
     InitOnContentProcessCreated();
 #endif
 }
 
 PMemoryReportRequestChild*
-ContentChild::AllocPMemoryReportRequestChild(const uint32_t& generation,
-                                             const bool &minimizeMemoryUsage,
+ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
+                                             const bool &aAnonymize,
+                                             const bool &aMinimizeMemoryUsage,
                                              const nsString& aDMDDumpIdent)
 {
-    MemoryReportRequestChild *actor = new MemoryReportRequestChild(generation, aDMDDumpIdent);
+    MemoryReportRequestChild *actor =
+        new MemoryReportRequestChild(aGeneration, aAnonymize, aDMDDumpIdent);
     actor->AddRef();
     return actor;
 }
 
 // This is just a wrapper for InfallibleTArray<MemoryReport> that implements
 // nsISupports, so it can be passed to nsIMemoryReporter::CollectReports.
 class MemoryReportsWrapper MOZ_FINAL : public nsISupports {
 public:
@@ -719,25 +725,27 @@ private:
 };
 NS_IMPL_ISUPPORTS(
   MemoryReportCallback
 , nsIMemoryReporterCallback
 )
 
 bool
 ContentChild::RecvPMemoryReportRequestConstructor(
-    PMemoryReportRequestChild* child,
-    const uint32_t& generation,
-    const bool& minimizeMemoryUsage,
+    PMemoryReportRequestChild* aChild,
+    const uint32_t& aGeneration,
+    const bool& aAnonymize,
+    const bool& aMinimizeMemoryUsage,
     const nsString& aDMDDumpIdent)
 {
-    MemoryReportRequestChild *actor = static_cast<MemoryReportRequestChild*>(child);
+    MemoryReportRequestChild *actor =
+        static_cast<MemoryReportRequestChild*>(aChild);
     nsresult rv;
 
-    if (minimizeMemoryUsage) {
+    if (aMinimizeMemoryUsage) {
         nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
         rv = mgr->MinimizeMemoryUsage(actor);
         // mgr will eventually call actor->Run()
     } else {
         rv = actor->Run();
     }
 
     return !NS_WARN_IF(NS_FAILED(rv));
@@ -754,17 +762,18 @@ NS_IMETHODIMP MemoryReportRequestChild::
     child->GetProcessName(process);
     child->AppendProcessId(process);
 
     // Run the reporters.  The callback will turn each measurement into a
     // MemoryReport.
     nsRefPtr<MemoryReportsWrapper> wrappedReports =
         new MemoryReportsWrapper(&reports);
     nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback(process);
-    mgr->GetReportsForThisProcessExtended(cb, wrappedReports, mDMDDumpIdent);
+    mgr->GetReportsForThisProcessExtended(cb, wrappedReports, mAnonymize,
+                                          mDMDDumpIdent);
 
     bool sent = Send__delete__(this, mGeneration, reports);
     return sent ? NS_OK : NS_ERROR_FAILURE;
 }
 
 bool
 ContentChild::RecvAudioChannelNotify()
 {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -147,26 +147,28 @@ public:
 
     virtual PHalChild* AllocPHalChild() MOZ_OVERRIDE;
     virtual bool DeallocPHalChild(PHalChild*) MOZ_OVERRIDE;
 
     virtual PIndexedDBChild* AllocPIndexedDBChild() MOZ_OVERRIDE;
     virtual bool DeallocPIndexedDBChild(PIndexedDBChild* aActor) MOZ_OVERRIDE;
 
     virtual PMemoryReportRequestChild*
-    AllocPMemoryReportRequestChild(const uint32_t& generation,
-                                   const bool &minimizeMemoryUsage,
-                                   const nsString &aDMDDumpIdent) MOZ_OVERRIDE;
+    AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
+                                   const bool& aAnonymize,
+                                   const bool& aMinimizeMemoryUsage,
+                                   const nsString& aDMDDumpIdent) MOZ_OVERRIDE;
     virtual bool
     DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor) MOZ_OVERRIDE;
 
     virtual bool
-    RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* child,
-                                        const uint32_t& generation,
-                                        const bool &minimizeMemoryUsage,
+    RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* aChild,
+                                        const uint32_t& aGeneration,
+                                        const bool& aAnonymize,
+                                        const bool &aMinimizeMemoryUsage,
                                         const nsString &aDMDDumpIdent) MOZ_OVERRIDE;
 
     virtual PCycleCollectWithLogsChild*
     AllocPCycleCollectWithLogsChild(const bool& aDumpAllTraces,
                                     const FileDescriptor& aGCLog,
                                     const FileDescriptor& aCCLog) MOZ_OVERRIDE;
     virtual bool
     DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* aActor) MOZ_OVERRIDE;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -449,27 +449,28 @@ public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIMEMORYREPORTER
 };
 
 NS_IMPL_ISUPPORTS(ContentParentsMemoryReporter, nsIMemoryReporter)
 
 NS_IMETHODIMP
 ContentParentsMemoryReporter::CollectReports(nsIMemoryReporterCallback* cb,
-                                             nsISupports* aClosure)
+                                             nsISupports* aClosure,
+                                             bool aAnonymize)
 {
     nsAutoTArray<ContentParent*, 16> cps;
     ContentParent::GetAllEvenIfDead(cps);
 
     for (uint32_t i = 0; i < cps.Length(); i++) {
         ContentParent* cp = cps[i];
         MessageChannel* channel = cp->GetIPCChannel();
 
         nsString friendlyName;
-        cp->FriendlyName(friendlyName);
+        cp->FriendlyName(friendlyName, aAnonymize);
 
         cp->AddRef();
         nsrefcnt refcnt = cp->Release();
 
         const char* channelStr = "no channel";
         uint32_t numQueuedMessages = 0;
         if (channel) {
             if (channel->Unsound_IsClosed()) {
@@ -2418,31 +2419,32 @@ ContentParent::Observe(nsISupports* aSub
     }
     else if (!strcmp(aTopic, "child-memory-reporter-request")) {
         bool isNuwa = false;
 #ifdef MOZ_NUWA_PROCESS
         isNuwa = IsNuwaProcess();
 #endif
         if (!isNuwa) {
             unsigned generation;
-            int minimize, identOffset = -1;
+            int anonymize, minimize, identOffset = -1;
             nsDependentString msg(aData);
             NS_ConvertUTF16toUTF8 cmsg(msg);
 
             if (sscanf(cmsg.get(),
-                       "generation=%x minimize=%d DMDident=%n",
-                       &generation, &minimize, &identOffset) < 2
+                       "generation=%x anonymize=%d minimize=%d DMDident=%n",
+                       &generation, &anonymize, &minimize, &identOffset) < 3
                 || identOffset < 0) {
                 return NS_ERROR_INVALID_ARG;
             }
             // The pre-%n part of the string should be all ASCII, so the byte
             // offset in identOffset should be correct as a char offset.
             MOZ_ASSERT(cmsg[identOffset - 1] == '=');
             unused << SendPMemoryReportRequestConstructor(
-              generation, minimize, nsString(Substring(msg, identOffset)));
+              generation, anonymize, minimize,
+              nsString(Substring(msg, identOffset)));
         }
     }
     else if (!strcmp(aTopic, "child-gc-request")){
         unused << SendGarbageCollect();
     }
     else if (!strcmp(aTopic, "child-cc-request")){
         unused << SendCycleCollect();
     }
@@ -2673,28 +2675,30 @@ ContentParent::KillHard()
 
 bool
 ContentParent::IsPreallocated()
 {
     return mAppManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL;
 }
 
 void
-ContentParent::FriendlyName(nsAString& aName)
+ContentParent::FriendlyName(nsAString& aName, bool aAnonymize)
 {
     aName.Truncate();
 #ifdef MOZ_NUWA_PROCESS
     if (IsNuwaProcess()) {
         aName.AssignLiteral("(Nuwa)");
     } else
 #endif
     if (IsPreallocated()) {
         aName.AssignLiteral("(Preallocated)");
     } else if (mIsForBrowser) {
         aName.AssignLiteral("Browser");
+    } else if (aAnonymize) {
+        aName.AssignLiteral("<anonymized-name>");
     } else if (!mAppName.IsEmpty()) {
         aName = mAppName;
     } else if (!mAppManifestURL.IsEmpty()) {
         aName.AssignLiteral("Unknown app: ");
         aName.Append(mAppManifestURL);
     } else {
         aName.AssignLiteral("???");
     }
@@ -2772,18 +2776,19 @@ ContentParent::RecvPIndexedDBConstructor
     IndexedDBParent* actor = static_cast<IndexedDBParent*>(aActor);
     actor->mFactory = factory;
     actor->mASCIIOrigin = factory->GetASCIIOrigin();
 
     return true;
 }
 
 PMemoryReportRequestParent*
-ContentParent::AllocPMemoryReportRequestParent(const uint32_t& generation,
-                                               const bool &minimizeMemoryUsage,
+ContentParent::AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
+                                               const bool &aAnonymize,
+                                               const bool &aMinimizeMemoryUsage,
                                                const nsString &aDMDDumpIdent)
 {
     MemoryReportRequestParent* parent = new MemoryReportRequestParent();
     return parent;
 }
 
 bool
 ContentParent::DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor)
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -203,17 +203,17 @@ public:
     bool IsPreallocated();
 
     /**
      * Get a user-friendly name for this ContentParent.  We make no guarantees
      * about this name: It might not be unique, apps can spoof special names,
      * etc.  So please don't use this name to make any decisions about the
      * ContentParent based on the value returned here.
      */
-    void FriendlyName(nsAString& aName);
+    void FriendlyName(nsAString& aName, bool aAnonymize = false);
 
     virtual void OnChannelError() MOZ_OVERRIDE;
 
     virtual PIndexedDBParent* AllocPIndexedDBParent() MOZ_OVERRIDE;
     virtual bool
     RecvPIndexedDBConstructor(PIndexedDBParent* aActor) MOZ_OVERRIDE;
 
     virtual PCrashReporterParent*
@@ -413,18 +413,19 @@ private:
     virtual bool RecvGetRandomValues(const uint32_t& length,
                                      InfallibleTArray<uint8_t>* randomValues) MOZ_OVERRIDE;
 
     virtual bool DeallocPHalParent(PHalParent*) MOZ_OVERRIDE;
 
     virtual bool DeallocPIndexedDBParent(PIndexedDBParent* aActor) MOZ_OVERRIDE;
 
     virtual PMemoryReportRequestParent*
-    AllocPMemoryReportRequestParent(const uint32_t& generation,
-                                    const bool &minimizeMemoryUsage,
+    AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
+                                    const bool &aAnonymize,
+                                    const bool &aMinimizeMemoryUsage,
                                     const nsString &aDMDDumpIdent) MOZ_OVERRIDE;
     virtual bool DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor) MOZ_OVERRIDE;
 
     virtual PCycleCollectWithLogsParent*
     AllocPCycleCollectWithLogsParent(const bool& aDumpAllTraces,
                                      const FileDescriptor& aGCLog,
                                      const FileDescriptor& aCCLog) MOZ_OVERRIDE;
     virtual bool
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -345,17 +345,18 @@ both:
 child:
     /**
      * Enable system-level sandboxing features, if available.  Can
      * usually only be performed zero or one times.  The child may
      * abnormally exit if this fails; the details are OS-specific.
      */
     async SetProcessSandbox();
 
-    PMemoryReportRequest(uint32_t generation, bool minimizeMemoryUsage, nsString DMDDumpIdent);
+    PMemoryReportRequest(uint32_t generation, bool anonymize,
+                         bool minimizeMemoryUsage, nsString DMDDumpIdent);
 
     /**
      * Notify the AudioChannelService in the child processes.
      */
     async AudioChannelNotify();
 
     async SpeakerManagerNotify();
 
--- a/dom/settings/SettingsManager.js
+++ b/dom/settings/SettingsManager.js
@@ -380,28 +380,29 @@ SettingsManager.prototype = {
     if (aTopic == "inner-window-destroyed") {
       let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
       if (wId == this.innerWindowID) {
         this.cleanup();
       }
     }
   },
 
-  collectReports: function(aCallback, aData) {
+  collectReports: function(aCallback, aData, aAnonymize) {
     for (var topic in this._callbacks) {
       let length = this._callbacks[topic].length;
       if (length == 0) {
         continue;
       }
 
       let path;
       if (length < 20) {
         path = "settings-observers";
       } else {
-        path = "settings-observers-suspect/referent(topic=" + topic + ")";
+        path = "settings-observers-suspect/referent(topic=" +
+               (aAnonymize ? "<anonymized>" : topic) + ")";
       }
 
       aCallback.callback("", path,
                          Ci.nsIMemoryReporter.KIND_OTHER,
                          Ci.nsIMemoryReporter.UNITS_COUNT,
                          this._callbacks[topic].length,
                          "The number of settings observers for this topic.",
                          aData);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1753,16 +1753,18 @@ public:
                                             (void *)js::GetCompartmentZone(aCompartment));
     extras->jsPathPrefix += js::IsAtomsCompartment(aCompartment)
                             ? NS_LITERAL_CSTRING("compartment(web-worker-atoms)/")
                             : NS_LITERAL_CSTRING("compartment(web-worker)/");
 
     // This should never be used when reporting with workers (hence the "?!").
     extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
 
+    extras->location = nullptr;
+
     aCompartmentStats->extra = extras;
   }
 };
 
 class MessagePortRunnable MOZ_FINAL : public WorkerRunnable
 {
   uint64_t mMessagePortSerial;
   bool mConnect;
@@ -1970,65 +1972,71 @@ struct WorkerPrivate::TimeoutInfo
 class WorkerPrivate::MemoryReporter MOZ_FINAL : public nsIMemoryReporter
 {
   NS_DECL_THREADSAFE_ISUPPORTS
 
   friend class WorkerPrivate;
 
   SharedMutex mMutex;
   WorkerPrivate* mWorkerPrivate;
-  nsCString mRtPath;
   bool mAlreadyMappedToAddon;
 
 public:
   MemoryReporter(WorkerPrivate* aWorkerPrivate)
   : mMutex(aWorkerPrivate->mMutex), mWorkerPrivate(aWorkerPrivate),
     mAlreadyMappedToAddon(false)
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
 
-    nsCString escapedDomain(aWorkerPrivate->Domain());
-    escapedDomain.ReplaceChar('/', '\\');
-
-    NS_ConvertUTF16toUTF8 escapedURL(aWorkerPrivate->ScriptURL());
-    escapedURL.ReplaceChar('/', '\\');
-
-    nsAutoCString addressString;
-    addressString.AppendPrintf("0x%p", static_cast<void*>(aWorkerPrivate));
-
-    mRtPath = NS_LITERAL_CSTRING("explicit/workers/workers(") +
-              escapedDomain + NS_LITERAL_CSTRING(")/worker(") +
-              escapedURL + NS_LITERAL_CSTRING(", ") + addressString +
-              NS_LITERAL_CSTRING(")/");
   }
 
   NS_IMETHOD
   CollectReports(nsIMemoryReporterCallback* aCallback,
-                 nsISupports* aClosure)
+                 nsISupports* aClosure, bool aAnonymize)
   {
     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);
+    // Assumes that WorkerJSRuntimeStats will hold a reference to |path|, and
+    // not a copy, as TryToMapAddon() may later modify if.
+    nsCString path;
+    WorkerJSRuntimeStats rtStats(path);
 
     {
       MutexAutoLock lock(mMutex);
 
-      TryToMapAddon();
+      path.AppendLiteral("explicit/workers/workers(");
+      if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
+        path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
+      } else {
+        nsCString escapedDomain(mWorkerPrivate->Domain());
+        if (escapedDomain.IsEmpty()) {
+          escapedDomain += "chrome";
+        } else {
+          escapedDomain.ReplaceChar('/', '\\');
+        }
+        path.Append(escapedDomain);
+        path.AppendLiteral(")/worker(");
+        NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
+        escapedURL.ReplaceChar('/', '\\');
+        path.Append(escapedURL);
+      }
+      path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
+
+      TryToMapAddon(path);
 
       if (!mWorkerPrivate ||
-          !mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats)) {
+          !mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats, aAnonymize)) {
         // Returning NS_OK here will effectively report 0 memory.
         return NS_OK;
       }
     }
 
-    return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, mRtPath,
-                                                 aCallback, aClosure);
+    return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, path,
+                                                 aCallback, aClosure,
+                                                 aAnonymize);
   }
 
 private:
   ~MemoryReporter()
   { }
 
   void
   Disable()
@@ -2037,17 +2045,17 @@ private:
     mMutex.AssertCurrentThreadOwns();
 
     NS_ASSERTION(mWorkerPrivate, "Disabled more than once!");
     mWorkerPrivate = nullptr;
   }
 
   // Only call this from the main thread and under mMutex lock.
   void
-  TryToMapAddon()
+  TryToMapAddon(nsACString &path)
   {
     AssertIsOnMainThread();
     mMutex.AssertCurrentThreadOwns();
 
     if (mAlreadyMappedToAddon || !mWorkerPrivate) {
       return;
     }
 
@@ -2073,17 +2081,17 @@ private:
         NS_FAILED(addonManager->MapURIToAddonID(scriptURI, addonId, &ok)) ||
         !ok) {
       return;
     }
 
     static const size_t explicitLength = strlen("explicit/");
     addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0);
     addonId += "/";
-    mRtPath.Insert(addonId, explicitLength);
+    path.Insert(addonId, explicitLength);
   }
 };
 
 NS_IMPL_ISUPPORTS(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
 
 WorkerPrivate::SyncLoopInfo::SyncLoopInfo(EventTarget* aEventTarget)
 : mEventTarget(aEventTarget), mCompleted(false), mResult(false)
 #ifdef DEBUG
@@ -4296,17 +4304,18 @@ WorkerPrivate::ScheduleDeletion(WorkerRa
       new TopLevelWorkerFinishedRunnable(this);
     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
       NS_WARNING("Failed to dispatch runnable!");
     }
   }
 }
 
 bool
-WorkerPrivate::BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats)
+WorkerPrivate::BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats,
+                                           bool aAnonymize)
 {
   AssertIsOnMainThread();
   mMutex.AssertCurrentThreadOwns();
   NS_ASSERTION(aRtStats, "Null RuntimeStats!");
 
   NS_ASSERTION(!mMemoryReporterRunning, "How can we get reentered here?!");
 
   // This signals the worker that it should block itself as soon as possible.
@@ -4329,17 +4338,17 @@ WorkerPrivate::BlockAndCollectRuntimeSta
 
   bool succeeded = false;
 
   // If mMemoryReporter is still set then we can do the actual report. Otherwise
   // we're trying to shut down and we don't want to do anything but clean up.
   if (mMemoryReporter) {
     // Don't hold the lock while doing the actual report.
     MutexAutoUnlock unlock(mMutex);
-    succeeded = JS::CollectRuntimeStats(rt, aRtStats, nullptr);
+    succeeded = JS::CollectRuntimeStats(rt, aRtStats, nullptr, aAnonymize);
   }
 
   NS_ASSERTION(mMemoryReporterRunning, "This isn't possible!");
   NS_ASSERTION(mBlockedForMemoryReporter, "Somehow we got unblocked!");
 
   // Tell the worker that it can now continue its execution.
   mMemoryReporterRunning = false;
 
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -943,17 +943,17 @@ public:
     WorkerNeverRan = 0,
     WorkerRan
   };
 
   void
   ScheduleDeletion(WorkerRanOrNot aRanOrNot);
 
   bool
-  BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats);
+  BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats, bool aAnonymize);
 
 #ifdef JS_GC_ZEAL
   void
   UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal, uint32_t aFrequency);
 #endif
 
   void
   GarbageCollectInternal(JSContext* aCx, bool aShrinking,
--- a/extensions/spellcheck/hunspell/src/mozHunspell.h
+++ b/extensions/spellcheck/hunspell/src/mozHunspell.h
@@ -98,17 +98,17 @@ public:
   nsresult Init();
 
   void LoadDictionaryList();
 
   // helper method for converting a word to the charset of the dictionary
   nsresult ConvertCharset(const char16_t* aStr, char ** aDst);
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData)
+                            nsISupports* aData, bool aAnonymize)
   {
     return MOZ_COLLECT_REPORT(
       "explicit/spell-check", KIND_HEAP, UNITS_BYTES, HunspellAllocator::MemoryAllocated(),
       "Memory used by the spell-checking engine.");
   }
 
 protected:
 
--- a/gfx/gl/GfxTexturesReporter.h
+++ b/gfx/gl/GfxTexturesReporter.h
@@ -37,17 +37,17 @@ public:
     };
 
     // When memory is used/freed for tile textures, call this method to update
     // the value reported by this memory reporter.
     static void UpdateAmount(MemoryUse action, GLenum format, GLenum type,
                              int32_t tileWidth, int32_t tileHeight);
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                              nsISupports* aData)
+                              nsISupports* aData, bool aAnonymize)
     {
         return MOZ_COLLECT_REPORT(
             "gfx-textures", KIND_OTHER, UNITS_BYTES, sAmount,
             "Memory used for storing GL textures.");
     }
 
 private:
     static int64_t sAmount;
--- a/gfx/layers/ipc/ISurfaceAllocator.h
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -205,17 +205,17 @@ public:
   }
 
   static void WillFree(void* aPointer)
   {
     sAmount -= MallocSizeOfOnFree(aPointer);
   }
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData)
+                            nsISupports* aData, bool aAnonymize)
   {
     return MOZ_COLLECT_REPORT(
       "explicit/gfx/heap-textures", KIND_HEAP, UNITS_BYTES, sAmount,
       "Heap memory shared between threads by texture clients and hosts.");
   }
 
 private:
   static mozilla::Atomic<size_t> sAmount;
--- a/gfx/layers/ipc/SharedBufferManagerParent.cpp
+++ b/gfx/layers/ipc/SharedBufferManagerParent.cpp
@@ -36,17 +36,17 @@ Atomic<uint32_t> SharedBufferManagerPare
 
 #ifdef MOZ_WIDGET_GONK
 class GrallocReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData)
+                            nsISupports* aData, bool aAnonymize)
   {
     map<base::ProcessId, SharedBufferManagerParent*>::iterator it;
     for (it = SharedBufferManagerParent::sManagers.begin(); it != SharedBufferManagerParent::sManagers.end(); it++) {
       base::ProcessId pid = it->first;
       SharedBufferManagerParent *mgr = it->second;
 
       std::map<int, android::sp<android::GraphicBuffer> >::iterator buf_it;
       for (buf_it = mgr->mBuffers.begin(); buf_it != mgr->mBuffers.end(); buf_it++) {
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -581,17 +581,17 @@ PR_STATIC_ASSERT(uint32_t(CAIRO_SURFACE_
 static int64_t gSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)] = { 0 };
 
 class SurfaceMemoryReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCb,
-                              nsISupports *aClosure)
+                              nsISupports *aClosure, bool aAnonymize)
     {
         const size_t len = ArrayLength(sSurfaceMemoryReporterAttrs);
         for (size_t i = 0; i < len; i++) {
             int64_t amount = gSurfaceMemoryUsed[i];
 
             if (amount != 0) {
                 const char *path = sSurfaceMemoryReporterAttrs[i].path;
                 const char *desc = sSurfaceMemoryReporterAttrs[i].description;
--- a/gfx/thebes/gfxAndroidPlatform.cpp
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -58,17 +58,17 @@ public:
 
     static void*
     Realloc(FT_Memory, long cur_size, long new_size, void* p)
     {
         return CountingRealloc(p, new_size);
     }
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                              nsISupports* aData)
+                              nsISupports* aData, bool aAnonymize)
     {
         return MOZ_COLLECT_REPORT(
             "explicit/freetype", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
             "Memory used by Freetype.");
     }
 };
 
 NS_IMPL_ISUPPORTS(FreetypeReporter, nsIMemoryReporter)
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1735,19 +1735,18 @@ gfxFontFamily::AddSizeOfIncludingThis(Ma
  * shaped-word caches to free up memory.
  */
 
 MOZ_DEFINE_MALLOC_SIZE_OF(FontCacheMallocSizeOf)
 
 NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter, nsIMemoryReporter)
 
 NS_IMETHODIMP
-gfxFontCache::MemoryReporter::CollectReports
-    (nsIMemoryReporterCallback* aCb,
-     nsISupports* aClosure)
+gfxFontCache::MemoryReporter::CollectReports(
+    nsIMemoryReporterCallback* aCb, nsISupports* aClosure, bool aAnonymize)
 {
     FontCacheSizes sizes;
 
     gfxFontCache::GetCache()->AddSizeOfIncludingThis(&FontCacheMallocSizeOf,
                                                      &sizes);
 
     aCb->Callback(EmptyCString(),
                   NS_LITERAL_CSTRING("explicit/gfx/font-cache"),
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -123,39 +123,41 @@ gfxFontListPrefObserver::Observe(nsISupp
     return NS_OK;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf)
 
 NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter)
 
 NS_IMETHODIMP
-gfxPlatformFontList::MemoryReporter::CollectReports
-    (nsIMemoryReporterCallback* aCb,
-     nsISupports* aClosure)
+gfxPlatformFontList::MemoryReporter::CollectReports(
+    nsIMemoryReporterCallback* aCb, nsISupports* aClosure, bool aAnonymize)
 {
     FontListSizes sizes;
     sizes.mFontListSize = 0;
     sizes.mFontTableCacheSize = 0;
     sizes.mCharMapsSize = 0;
 
     gfxPlatformFontList::PlatformFontList()->AddSizeOfIncludingThis(&FontListMallocSizeOf,
                                                                     &sizes);
 
-    aCb->Callback(EmptyCString(),
-                  NS_LITERAL_CSTRING("explicit/gfx/font-list"),
-                  KIND_HEAP, UNITS_BYTES, sizes.mFontListSize,
-                  NS_LITERAL_CSTRING("Memory used to manage the list of font families and faces."),
-                  aClosure);
+    nsresult rv;
+    rv = aCb->Callback(EmptyCString(),
+                       NS_LITERAL_CSTRING("explicit/gfx/font-list"),
+                       KIND_HEAP, UNITS_BYTES, sizes.mFontListSize,
+                       NS_LITERAL_CSTRING("Memory used to manage the list of font families and faces."),
+                       aClosure);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    aCb->Callback(EmptyCString(),
-                  NS_LITERAL_CSTRING("explicit/gfx/font-charmaps"),
-                  KIND_HEAP, UNITS_BYTES, sizes.mCharMapsSize,
-                  NS_LITERAL_CSTRING("Memory used to record the character coverage of individual fonts."),
-                  aClosure);
+    rv = aCb->Callback(EmptyCString(),
+                       NS_LITERAL_CSTRING("explicit/gfx/font-charmaps"),
+                       KIND_HEAP, UNITS_BYTES, sizes.mCharMapsSize,
+                       NS_LITERAL_CSTRING("Memory used to record the character coverage of individual fonts."),
+                       aClosure);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     if (sizes.mFontTableCacheSize) {
         aCb->Callback(EmptyCString(),
                       NS_LITERAL_CSTRING("explicit/gfx/font-tables"),
                       KIND_HEAP, UNITS_BYTES, sizes.mFontTableCacheSize,
                       NS_LITERAL_CSTRING("Memory used for cached font metrics and layout tables."),
                       aClosure);
     }
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -82,17 +82,17 @@ static const int kSupportedFeatureLevels
     D3D10_FEATURE_LEVEL_9_3 };
 
 class GfxD2DSurfaceReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                              nsISupports* aData)
+                              nsISupports* aData, bool aAnonymize)
     {
         nsresult rv;
 
         int64_t amount = cairo_d2d_get_image_surface_cache_usage();
         rv = MOZ_COLLECT_REPORT(
             "gfx-d2d-surface-cache", KIND_OTHER, UNITS_BYTES, amount,
             "Memory used by the Direct2D internal surface cache.");
         NS_ENSURE_SUCCESS(rv, rv);
@@ -114,17 +114,17 @@ NS_IMPL_ISUPPORTS(GfxD2DSurfaceReporter,
 #endif
 
 class GfxD2DVramReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                              nsISupports* aData)
+                              nsISupports* aData, bool aAnonymize)
     {
         nsresult rv;
 
         rv = MOZ_COLLECT_REPORT(
             "gfx-d2d-vram-draw-target", KIND_OTHER, UNITS_BYTES,
             Factory::GetD2DVRAMUsageDrawTarget(),
             "Video memory used by D2D DrawTargets.");
         NS_ENSURE_SUCCESS(rv, rv);
@@ -170,30 +170,30 @@ class GPUAdapterReporter : public nsIMem
         return result;
     }
 
 public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD
     CollectReports(nsIMemoryReporterCallback* aCb,
-                   nsISupports* aClosure)
+                   nsISupports* aClosure, bool aAnonymize)
     {
         HANDLE ProcessHandle = GetCurrentProcess();
 
         int64_t dedicatedBytesUsed = 0;
         int64_t sharedBytesUsed = 0;
         int64_t committedBytesUsed = 0;
         IDXGIAdapter *DXGIAdapter;
 
         HMODULE gdi32Handle;
         PFND3DKMTQS queryD3DKMTStatistics;
 
         // GPU memory reporting is not available before Windows 7
-        if (!IsWin7OrLater()) 
+        if (!IsWin7OrLater())
             return NS_OK;
 
         if (gdi32Handle = LoadLibrary(TEXT("gdi32.dll")))
             queryD3DKMTStatistics = (PFND3DKMTQS)GetProcAddress(gdi32Handle, "D3DKMTQueryStatistics");
 
         if (queryD3DKMTStatistics && GetDXGIAdapter(&DXGIAdapter)) {
             // Most of this block is understood thanks to wj32's work on Process Hacker
 
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -361,17 +361,18 @@ public:
                                         CachedSurface*    aSurface,
                                         void*             aCache)
   {
     static_cast<SurfaceCacheImpl*>(aCache)->StopTracking(aSurface);
     return PL_DHASH_NEXT;
   }
 
   NS_IMETHOD
-  CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+  CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+                 bool aAnonymize)
   {
     return MOZ_COLLECT_REPORT(
       "imagelib-surface-cache", KIND_OTHER, UNITS_BYTES,
       SizeOfSurfacesEstimate(),
       "Memory used by the imagelib temporary surface cache.");
   }
 
   // XXX(seth): This is currently only an estimate and, since we don't know
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -51,27 +51,29 @@ using namespace mozilla::image;
 MOZ_DEFINE_MALLOC_SIZE_OF(ImagesMallocSizeOf)
 
 class imgMemoryReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aHandleReport,
-                            nsISupports *aData)
+                            nsISupports *aData, bool aAnonymize)
   {
     nsresult rv;
     ImageSizes chrome;
     ImageSizes content;
 
     for (uint32_t i = 0; i < mKnownLoaders.Length(); i++) {
       mKnownLoaders[i]->mChromeCache.EnumerateRead(EntryImageSizes, &chrome);
       mKnownLoaders[i]->mCache.EnumerateRead(EntryImageSizes, &content);
     }
 
+    // Note that we only need to anonymize content image URIs.
+
     rv = ReportInfoArray(aHandleReport, aData, chrome.mRasterUsedImageInfo,
                          "images/chrome/raster/used");
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = ReportInfoArray(aHandleReport, aData, chrome.mRasterUnusedImageInfo,
                          "images/chrome/raster/unused");
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -79,29 +81,29 @@ public:
                          "images/chrome/vector/used/documents");
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = ReportInfoArray(aHandleReport, aData, chrome.mVectorUnusedImageDocInfo,
                          "images/chrome/vector/unused/documents");
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = ReportInfoArray(aHandleReport, aData, content.mRasterUsedImageInfo,
-                         "images/content/raster/used");
+                         "images/content/raster/used", aAnonymize);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = ReportInfoArray(aHandleReport, aData, content.mRasterUnusedImageInfo,
-                         "images/content/raster/unused");
+                         "images/content/raster/unused", aAnonymize);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = ReportInfoArray(aHandleReport, aData, content.mVectorUsedImageDocInfo,
-                         "images/content/vector/used/documents");
+                         "images/content/vector/used/documents", aAnonymize);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = ReportInfoArray(aHandleReport, aData, content.mVectorUnusedImageDocInfo,
-                         "images/content/vector/unused/documents");
+                         "images/content/vector/unused/documents", aAnonymize);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 
   static int64_t ImagesContentUsedUncompressedDistinguishedAmount()
   {
     size_t n = 0;
@@ -196,37 +198,43 @@ private:
                        Sizes aSizes);
 
   // This is used to report all images of a single kind, e.g. all
   // "chrome/raster/used" images.
   template <typename Sizes>
   nsresult ReportInfoArray(nsIMemoryReporterCallback *aHandleReport,
                            nsISupports *aData,
                            const nsTArray<ImageInfo<Sizes> > &aInfoArray,
-                           const char *aPathPartStr)
+                           const char *aPathPartStr, bool aAnonymize = false)
   {
     nsresult rv;
     Sizes totalSizes;
     Sizes nonNotableSizes;
 
     nsCString pathPart(aPathPartStr);
     nsCString explicitPathPrefix(aPathPartStr);
     explicitPathPrefix.Insert("explicit/", 0);
 
     // Report notable images, and compute total and non-notable aggregate sizes.
     for (uint32_t i = 0; i < aInfoArray.Length(); i++) {
       ImageInfo<Sizes> info = aInfoArray[i];
-      // info.mURI can be a data: URI, and thus extremely long. Truncate if
-      // necessary.
-      static const size_t max = 256;
-      if (info.mURI.Length() > max) {
-        info.mURI.Truncate(max);
-        info.mURI.AppendLiteral(" (truncated)");
+
+      if (aAnonymize) {
+        info.mURI.Truncate();
+        info.mURI.AppendPrintf("<anonymized-%u>", i);
+      } else {
+        // info.mURI can be a data: URI, and thus extremely long. Truncate if
+        // necessary.
+        static const size_t max = 256;
+        if (info.mURI.Length() > max) {
+          info.mURI.Truncate(max);
+          info.mURI.AppendLiteral(" (truncated)");
+        }
+        info.mURI.ReplaceChar('/', '\\');
       }
-      info.mURI.ReplaceChar('/', '\\');
 
       totalSizes.add(info.mSizes);
 
       if (!info.mSizes.isNotable()) {
         nonNotableSizes.add(info.mSizes);
       } else {
         // Report the notable image.
         rv = ReportSizes(aHandleReport, aData, explicitPathPrefix,
--- a/image/test/mochitest/test_bug601470.html
+++ b/image/test/mochitest/test_bug601470.html
@@ -25,17 +25,17 @@ 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;
   }
 
-  mgr.getReportsForThisProcess(handleReport, null);
+  mgr.getReportsForThisProcess(handleReport, null, /* anonymize = */ false);
 
   ok(amount > 0, "we should be using a nonzero amount of memory");
   ok(true, "yay, didn't crash!");
 
   SimpleTest.finish();
 }
 
 </script>
--- a/ipc/glue/SharedMemory.cpp
+++ b/ipc/glue/SharedMemory.cpp
@@ -18,17 +18,18 @@ static Atomic<size_t> gShmemAllocated;
 static Atomic<size_t> gShmemMapped;
 
 class ShmemReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
   NS_IMETHOD
-  CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+  CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+                 bool aAnonymize)
   {
     nsresult rv;
     rv = MOZ_COLLECT_REPORT(
       "shmem-allocated", KIND_OTHER, UNITS_BYTES, gShmemAllocated,
       "Memory shared with other processes that is accessible (but not "
       "necessarily mapped).");
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -630,17 +630,17 @@ class ObjectPrivateVisitor
     GetISupportsFun getISupports_;
 
     explicit ObjectPrivateVisitor(GetISupportsFun getISupports)
       : getISupports_(getISupports)
     {}
 };
 
 extern JS_PUBLIC_API(bool)
-CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv);
+CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv, bool anonymize);
 
 extern JS_PUBLIC_API(size_t)
 SystemCompartmentCount(JSRuntime *rt);
 
 extern JS_PUBLIC_API(size_t)
 UserCompartmentCount(JSRuntime *rt);
 
 extern JS_PUBLIC_API(size_t)
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -191,17 +191,24 @@ NotableScriptSourceInfo &NotableScriptSo
 
 typedef HashSet<ScriptSource *, DefaultHasher<ScriptSource *>, SystemAllocPolicy> SourceSet;
 
 struct StatsClosure
 {
     RuntimeStats *rtStats;
     ObjectPrivateVisitor *opv;
     SourceSet seenSources;
-    StatsClosure(RuntimeStats *rt, ObjectPrivateVisitor *v) : rtStats(rt), opv(v) {}
+    bool anonymize;
+
+    StatsClosure(RuntimeStats *rt, ObjectPrivateVisitor *v, bool anon)
+      : rtStats(rt),
+        opv(v),
+        anonymize(anon)
+    {}
+
     bool init() {
         return seenSources.init();
     }
 };
 
 static void
 DecommittedArenasChunkCallback(JSRuntime *rt, void *data, gc::Chunk *chunk)
 {
@@ -282,19 +289,23 @@ StatsArenaCallback(JSRuntime *rt, void *
 }
 
 static CompartmentStats *
 GetCompartmentStats(JSCompartment *comp)
 {
     return static_cast<CompartmentStats *>(comp->compartmentStats);
 }
 
+// FineGrained is used for normal memory reporting.  CoarseGrained is used by
+// AddSizeOfTab(), which aggregates all the measurements into a handful of
+// high-level numbers, which means that fine-grained reporting would be a waste
+// of effort.
 enum Granularity {
-    FineGrained,    // Corresponds to CollectRuntimeStats()
-    CoarseGrained   // Corresponds to AddSizeOfTab()
+    FineGrained,
+    CoarseGrained
 };
 
 // The various kinds of hashing are expensive, and the results are unused when
 // doing coarse-grained measurements. Skipping them more than doubles the
 // profile speed for complex pages such as gmail.com.
 template <Granularity granularity>
 static void
 StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind,
@@ -331,17 +342,20 @@ StatsCellCallback(JSRuntime *rt, void *d
 
         JS::StringInfo info;
         info.gcHeap = thingSize;
         info.mallocHeap = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
         info.numCopies = 1;
 
         zStats->stringInfo.add(info);
 
-        if (granularity == FineGrained) {
+        // The primary use case for anonymization is automated crash submission
+        // (to help detect OOM crashes). In that case, we don't want to pay the
+        // memory cost required to do notable string detection.
+        if (granularity == FineGrained && !closure->anonymize) {
             ZoneStats::StringsHashMap::AddPtr p = zStats->allStrings->lookupForAdd(str);
             if (!p) {
                 // Ignore failure -- we just won't record the string as notable.
                 (void)zStats->allStrings->add(p, str, info);
             } else {
                 p->value().add(info);
             }
         }
@@ -527,17 +541,18 @@ FindNotableScriptSources(JS::RuntimeSize
     // Delete |allScriptSources| now, rather than waiting for zStats's
     // destruction, to reduce peak memory consumption during reporting.
     js_delete(runtime.allScriptSources);
     runtime.allScriptSources = nullptr;
     return true;
 }
 
 JS_PUBLIC_API(bool)
-JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv)
+JS::CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv,
+                        bool anonymize)
 {
     if (!rtStats->compartmentStatsVector.reserve(rt->numCompartments))
         return false;
 
     if (!rtStats->zoneStatsVector.reserve(rt->gc.zones.length()))
         return false;
 
     rtStats->gcHeapChunkTotal =
@@ -545,21 +560,24 @@ JS::CollectRuntimeStats(JSRuntime *rt, R
 
     rtStats->gcHeapUnusedChunks =
         size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize;
 
     IterateChunks(rt, &rtStats->gcHeapDecommittedArenas,
                   DecommittedArenasChunkCallback);
 
     // Take the per-compartment measurements.
-    StatsClosure closure(rtStats, opv);
+    StatsClosure closure(rtStats, opv, anonymize);
     if (!closure.init())
         return false;
-    IterateZonesCompartmentsArenasCells(rt, &closure, StatsZoneCallback, StatsCompartmentCallback,
-                                        StatsArenaCallback, StatsCellCallback<FineGrained>);
+    IterateZonesCompartmentsArenasCells(rt, &closure,
+                                        StatsZoneCallback,
+                                        StatsCompartmentCallback,
+                                        StatsArenaCallback,
+                                        StatsCellCallback<FineGrained>);
 
     // Take the "explicit/js/runtime/" measurements.
     rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime);
 
     if (!FindNotableScriptSources(rtStats->runtime))
         return false;
 
     ZoneStatsVector &zs = rtStats->zoneStatsVector;
@@ -669,18 +687,19 @@ AddSizeOfTab(JSRuntime *rt, HandleObject
     JS::Zone *zone = GetObjectZone(obj);
 
     if (!rtStats.compartmentStatsVector.reserve(zone->compartments.length()))
         return false;
 
     if (!rtStats.zoneStatsVector.reserve(1))
         return false;
 
-    // Take the per-compartment measurements.
-    StatsClosure closure(&rtStats, opv);
+    // Take the per-compartment measurements. No need to anonymize because
+    // these measurements will be aggregated.
+    StatsClosure closure(&rtStats, opv, /* anonymize = */ false);
     if (!closure.init())
         return false;
     IterateZoneCompartmentsArenasCells(rt, zone, &closure, StatsZoneCallback,
                                        StatsCompartmentCallback, StatsArenaCallback,
                                        StatsCellCallback<CoarseGrained>);
 
     JS_ASSERT(rtStats.zoneStatsVector.length() == 1);
     rtStats.zTotals.addSizes(rtStats.zoneStatsVector[0]);
--- a/js/xpconnect/src/XPCJSMemoryReporter.h
+++ b/js/xpconnect/src/XPCJSMemoryReporter.h
@@ -19,14 +19,15 @@ typedef nsDataHashtable<nsUint64HashKey,
 // because it's invoked by nsWindowMemoryReporter in order to get |windowPaths|
 // in CollectReports.
 class JSReporter
 {
 public:
     static nsresult CollectReports(WindowPaths *windowPaths,
                                    WindowPaths *topWindowPaths,
                                    nsIMemoryReporterCallback *cb,
-                                   nsISupports *closure);
+                                   nsISupports *closure,
+                                   bool anonymize);
 };
 
 }
 
 #endif
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1452,16 +1452,17 @@ XPCJSRuntime::CustomOutOfMemoryCallback(
     nsCOMPtr<nsIMemoryInfoDumper> dumper =
         do_GetService("@mozilla.org/memory-info-dumper;1");
     if (!dumper) {
         return;
     }
 
     // If this fails, it fails silently.
     dumper->DumpMemoryInfoToTempDir(NS_LITERAL_STRING("due-to-JS-OOM"),
+                                    /* anonymize = */ false,
                                     /* minimizeMemoryUsage = */ false);
 }
 
 void
 XPCJSRuntime::CustomLargeAllocationFailureCallback()
 {
     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     if (os) {
@@ -1676,37 +1677,68 @@ XPCJSRuntime::~XPCJSRuntime()
     for (uint32_t i = 0; i < XPCCCX_STRING_CACHE_SIZE; ++i) {
         MOZ_ASSERT(mScratchStrings[i].empty(), "Short lived string still in use");
     }
 #endif
 
     Preferences::UnregisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
 }
 
+// If |*anonymizeID| is non-zero and this is a user compartment, the name will
+// be anonymized.
 static void
-GetCompartmentName(JSCompartment *c, nsCString &name, bool replaceSlashes)
+GetCompartmentName(JSCompartment *c, nsCString &name, int *anonymizeID,
+                   bool replaceSlashes)
 {
     if (js::IsAtomsCompartment(c)) {
         name.AssignLiteral("atoms");
+    } else if (*anonymizeID && !js::IsSystemCompartment(c)) {
+        name.AppendPrintf("<anonymized-%d>", *anonymizeID);
+        *anonymizeID += 1;
     } else if (JSPrincipals *principals = JS_GetCompartmentPrincipals(c)) {
         nsJSPrincipals::get(principals)->GetScriptLocation(name);
 
         // If the compartment's location (name) differs from the principal's
         // script location, append the compartment's location to allow
         // differentiation of multiple compartments owned by the same principal
         // (e.g. components owned by the system or null principal).
         CompartmentPrivate *compartmentPrivate = GetCompartmentPrivate(c);
         if (compartmentPrivate) {
             const nsACString& location = compartmentPrivate->GetLocation();
             if (!location.IsEmpty() && !location.Equals(name)) {
                 name.AppendLiteral(", ");
                 name.Append(location);
             }
         }
 
+        // We might have a file:// URL that includes paths from the local
+        // filesystem, which should be omitted if we're anonymizing.
+        if (*anonymizeID) {
+            static const char *filePrefix = "file://";
+            int filePos = name.Find(filePrefix);
+            if (filePos >= 0) {
+                int pathPos = filePos + strlen(filePrefix);
+                int lastSlashPos = -1;
+                for (int i = pathPos; i < int(name.Length()); i++) {
+                    if (name[i] == '/' || name[i] == '\\') {
+                        lastSlashPos = i;
+                    }
+                }
+                if (lastSlashPos != -1) {
+                    name.ReplaceASCII(pathPos, lastSlashPos - pathPos,
+                                      "<anonymized>");
+                } else {
+                    // Something went wrong. Anonymize the entire path to be
+                    // safe.
+                    name.Truncate(pathPos);
+                    name += "<anonymized?!>";
+                }
+            }
+        }
+
         // A hack: replace forward slashes with '\\' so they aren't
         // treated as path separators.  Users of the reporters
         // (such as about:memory) have to undo this change.
         if (replaceSlashes)
             name.ReplaceChar('/', '\\');
     } else {
         name.AssignLiteral("null-principal");
     }
@@ -1742,17 +1774,17 @@ JSMainRuntimeCompartmentsUserDistinguish
 }
 
 class JSMainRuntimeTemporaryPeakReporter MOZ_FINAL : public nsIMemoryReporter
 {
   public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                              nsISupports* aData)
+                              nsISupports* aData, bool aAnonymize)
     {
         return MOZ_COLLECT_REPORT("js-main-runtime-temporary-peak",
             KIND_OTHER, UNITS_BYTES,
             JSMainRuntimeTemporaryPeakDistinguishedAmount(),
             "Peak transient data size in the main JSRuntime (the current size "
             "of which is reported as "
             "'explicit/js-non-window/runtime/temporary').");
     }
@@ -1845,17 +1877,19 @@ NS_IMPL_ISUPPORTS(JSMainRuntimeTemporary
 MOZ_DEFINE_MALLOC_SIZE_OF(JSMallocSizeOf)
 
 namespace xpc {
 
 static nsresult
 ReportZoneStats(const JS::ZoneStats &zStats,
                 const xpc::ZoneStatsExtras &extras,
                 nsIMemoryReporterCallback *cb,
-                nsISupports *closure, size_t *gcTotalOut = nullptr)
+                nsISupports *closure,
+                bool anonymize,
+                size_t *gcTotalOut = nullptr)
 {
     const nsAutoCString& pathPrefix = extras.pathPrefix;
     size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
 
     MOZ_ASSERT(!gcTotalOut == zStats.isTotals);
 
     ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
         zStats.gcHeapArenaAdmin,
@@ -1901,16 +1935,21 @@ ReportZoneStats(const JS::ZoneStats &zSt
     #define MAYBE_OVERALLOCATED \
         "Sometimes over-allocated to simplify string concatenation."
 
     for (size_t i = 0; i < zStats.notableStrings.length(); i++) {
         const JS::NotableStringInfo& info = zStats.notableStrings[i];
 
         MOZ_ASSERT(!zStats.isTotals);
 
+        // We don't do notable string detection when anonymizing, because
+        // there's a good chance its for crash submission, and the memory
+        // required for notable string detection is high.
+        MOZ_ASSERT(!anonymize);
+
         nsDependentCString notableString(info.buffer);
 
         // Viewing about:memory generates many notable strings which contain
         // "string(length=".  If we report these as notable, then we'll create
         // even more notable strings the next time we open about:memory (unless
         // there's a GC in the meantime), and so on ad infinitum.
         //
         // To avoid cluttering up about:memory like this, we stick notable
@@ -1944,17 +1983,17 @@ ReportZoneStats(const JS::ZoneStats &zSt
         if (info.mallocHeap > 0) {
             REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap"),
                 KIND_HEAP, info.mallocHeap,
                 "Non-inline string characters. " MAYBE_OVERALLOCATED);
         }
     }
 
     nsCString nonNotablePath = pathPrefix;
-    nonNotablePath += zStats.isTotals
+    nonNotablePath += (zStats.isTotals || anonymize)
                     ? NS_LITERAL_CSTRING("strings/")
                     : NS_LITERAL_CSTRING("strings/string(<non-notable strings>)/");
 
     if (zStats.stringInfo.gcHeap > 0) {
         REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap"),
             zStats.stringInfo.gcHeap,
             "Strings. " MAYBE_INLINE);
     }
@@ -2268,27 +2307,29 @@ ReportScriptSourceStats(const ScriptSour
     return NS_OK;
 }
 
 static nsresult
 ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
                                  const nsACString &rtPath,
                                  amIAddonManager* addonManager,
                                  nsIMemoryReporterCallback *cb,
-                                 nsISupports *closure, size_t *rtTotalOut)
+                                 nsISupports *closure,
+                                 bool anonymize,
+                                 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];
         const xpc::ZoneStatsExtras *extras =
           static_cast<const xpc::ZoneStatsExtras*>(zStats.extra);
-        rv = ReportZoneStats(zStats, *extras, cb, closure, &gcTotal);
+        rv = ReportZoneStats(zStats, *extras, cb, closure, anonymize, &gcTotal);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
         JS::CompartmentStats cStats = rtStats.compartmentStatsVector[i];
         const xpc::CompartmentStatsExtras *extras =
             static_cast<const xpc::CompartmentStatsExtras*>(cStats.extra);
         rv = ReportCompartmentStats(cStats, *extras, addonManager, cb, closure,
@@ -2351,19 +2392,24 @@ ReportJSRuntimeExplicitTreeStats(const J
         const JS::NotableScriptSourceInfo& scriptSourceInfo =
             rtStats.runtime.notableScriptSources[i];
 
         // Escape / to \ before we put the filename into the memory reporter
         // path, because we don't want any forward slashes in the string to
         // count as path separators. Consumers of memory reporters (e.g.
         // about:memory) will convert them back to / after doing path
         // splitting.
-        nsDependentCString filename(scriptSourceInfo.filename_);
-        nsCString escapedFilename(filename);
-        escapedFilename.ReplaceSubstring("/", "\\");
+        nsCString escapedFilename;
+        if (anonymize) {
+            escapedFilename.AppendPrintf("<anonymized-source-%d>", int(i));
+        } else {
+            nsDependentCString filename(scriptSourceInfo.filename_);
+            escapedFilename.Append(filename);
+            escapedFilename.ReplaceSubstring("/", "\\");
+        }
 
         nsCString notablePath = rtPath +
             nsPrintfCString("runtime/script-sources/source(scripts=%d, %s)/",
                             scriptSourceInfo.numScripts, escapedFilename.get());
 
         rv = ReportScriptSourceStats(scriptSourceInfo, notablePath,
                                      cb, closure, rtTotal);
         NS_ENSURE_SUCCESS(rv, rv);
@@ -2469,66 +2515,68 @@ ReportJSRuntimeExplicitTreeStats(const J
 
     return NS_OK;
 }
 
 nsresult
 ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats,
                                  const nsACString &rtPath,
                                  nsIMemoryReporterCallback *cb,
-                                 nsISupports *closure, size_t *rtTotalOut)
+                                 nsISupports *closure,
+                                 bool anonymize,
+                                 size_t *rtTotalOut)
 {
     nsCOMPtr<amIAddonManager> am;
     if (XRE_GetProcessType() == GeckoProcessType_Default) {
         // Only try to access the service from the main process.
         am = do_GetService("@mozilla.org/addons/integration;1");
     }
-    return ReportJSRuntimeExplicitTreeStats(rtStats, rtPath, am.get(), cb,
-                                            closure, rtTotalOut);
+    return ReportJSRuntimeExplicitTreeStats(rtStats, rtPath, am.get(),
+                                            cb, closure, anonymize, rtTotalOut);
 }
 
 
 } // namespace xpc
 
 class JSMainRuntimeCompartmentsReporter MOZ_FINAL : public nsIMemoryReporter
 {
   public:
     NS_DECL_ISUPPORTS
 
-    typedef js::Vector<nsCString, 0, js::SystemAllocPolicy> Paths;
-
-    static void CompartmentCallback(JSRuntime *rt, void* data, JSCompartment *c) {
+    struct Data {
+        int anonymizeID;
+        js::Vector<nsCString, 0, js::SystemAllocPolicy> paths;
+    };
+
+    static void CompartmentCallback(JSRuntime *rt, void* vdata, JSCompartment *c) {
         // silently ignore OOM errors
-        Paths *paths = static_cast<Paths *>(data);
+        Data *data = static_cast<Data *>(vdata);
         nsCString path;
-        GetCompartmentName(c, path, true);
+        GetCompartmentName(c, path, &data->anonymizeID, /* replaceSlashes = */ true);
         path.Insert(js::IsSystemCompartment(c)
                     ? NS_LITERAL_CSTRING("js-main-runtime-compartments/system/")
                     : NS_LITERAL_CSTRING("js-main-runtime-compartments/user/"),
                     0);
-        paths->append(path);
+        data->paths.append(path);
     }
 
     NS_IMETHOD CollectReports(nsIMemoryReporterCallback *cb,
-                              nsISupports *closure)
+                              nsISupports *closure, bool anonymize)
     {
         // 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.
-
-        Paths paths;
+        Data data;
+        data.anonymizeID = anonymize ? 1 : 0;
         JS_IterateCompartments(nsXPConnect::GetRuntimeInstance()->Runtime(),
-                               &paths, CompartmentCallback);
-
-        // Report.
-        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,
+                               &data, CompartmentCallback);
+
+        for (size_t i = 0; i < data.paths.length(); i++)
+            REPORT(nsCString(data.paths[i]), KIND_OTHER, UNITS_COUNT, 1,
                 "A live compartment in the main JSRuntime.");
 
         return NS_OK;
     }
 };
 
 NS_IMPL_ISUPPORTS(JSMainRuntimeCompartmentsReporter, nsIMemoryReporter)
 
@@ -2588,24 +2636,26 @@ StartsWithExplicit(nsACString& s)
 }
 #endif
 
 class XPCJSRuntimeStats : public JS::RuntimeStats
 {
     WindowPaths *mWindowPaths;
     WindowPaths *mTopWindowPaths;
     bool mGetLocations;
+    int mAnonymizeID;
 
   public:
     XPCJSRuntimeStats(WindowPaths *windowPaths, WindowPaths *topWindowPaths,
-                      bool getLocations)
+                      bool getLocations, bool anonymize)
       : JS::RuntimeStats(JSMallocSizeOf),
         mWindowPaths(windowPaths),
         mTopWindowPaths(topWindowPaths),
-        mGetLocations(getLocations)
+        mGetLocations(getLocations),
+        mAnonymizeID(anonymize ? 1 : 0)
     {}
 
     ~XPCJSRuntimeStats() {
         for (size_t i = 0; i != compartmentStatsVector.length(); ++i)
             delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
 
 
         for (size_t i = 0; i != zoneStatsVector.length(); ++i)
@@ -2641,17 +2691,17 @@ class XPCJSRuntimeStats : public JS::Run
         zStats->extra = extras;
     }
 
     virtual void initExtraCompartmentStats(JSCompartment *c,
                                            JS::CompartmentStats *cstats) MOZ_OVERRIDE
     {
         xpc::CompartmentStatsExtras *extras = new xpc::CompartmentStatsExtras;
         nsCString cName;
-        GetCompartmentName(c, cName, true);
+        GetCompartmentName(c, cName, &mAnonymizeID, /* replaceSlashes = */ true);
         if (mGetLocations) {
             CompartmentPrivate *cp = GetCompartmentPrivate(c);
             if (cp)
               cp->GetLocationURI(CompartmentPrivate::LocationHintAddon,
                                  getter_AddRefs(extras->location));
             // Note: cannot use amIAddonManager implementation at this point,
             // as it is a JS service and the JS heap is currently not idle.
             // Otherwise, we could have computed the add-on id at this point.
@@ -2712,36 +2762,41 @@ class XPCJSRuntimeStats : public JS::Run
         cstats->extra = extras;
     }
 };
 
 nsresult
 JSReporter::CollectReports(WindowPaths *windowPaths,
                            WindowPaths *topWindowPaths,
                            nsIMemoryReporterCallback *cb,
-                           nsISupports *closure)
+                           nsISupports *closure,
+                           bool anonymize)
 {
     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.
 
     nsCOMPtr<amIAddonManager> addonManager;
     if (XRE_GetProcessType() == GeckoProcessType_Default) {
         // Only try to access the service from the main process.
         addonManager = do_GetService("@mozilla.org/addons/integration;1");
     }
     bool getLocations = !!addonManager;
-    XPCJSRuntimeStats rtStats(windowPaths, topWindowPaths, getLocations);
+    XPCJSRuntimeStats rtStats(windowPaths, topWindowPaths, getLocations,
+                              anonymize);
     OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
-    if (!JS::CollectRuntimeStats(xpcrt->Runtime(), &rtStats, &orphanReporter))
+    if (!JS::CollectRuntimeStats(xpcrt->Runtime(), &rtStats, &orphanReporter,
+                                 anonymize))
+    {
         return NS_ERROR_FAILURE;
+    }
 
     size_t xpconnect = xpcrt->SizeOfIncludingThis(JSMallocSizeOf);
 
     XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf);
     XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(&sizeInfo);
 
     mozJSComponentLoader* loader = mozJSComponentLoader::Get();
     size_t jsComponentLoaderSize = loader ? loader->SizeOfIncludingThis(JSMallocSizeOf) : 0;
@@ -2749,30 +2804,30 @@ JSReporter::CollectReports(WindowPaths *
     // This is the second step (see above).  First we report stuff in the
     // "explicit" tree, then we report other stuff.
 
     nsresult rv;
     size_t rtTotal = 0;
     rv = xpc::ReportJSRuntimeExplicitTreeStats(rtStats,
                                                NS_LITERAL_CSTRING("explicit/js-non-window/"),
                                                addonManager, cb, closure,
-                                               &rtTotal);
+                                               anonymize, &rtTotal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Report the sums of the compartment numbers.
     xpc::CompartmentStatsExtras cExtrasTotal;
     cExtrasTotal.jsPathPrefix.AssignLiteral("js-main-runtime/compartments/");
     cExtrasTotal.domPathPrefix.AssignLiteral("window-objects/dom/");
     rv = ReportCompartmentStats(rtStats.cTotals, cExtrasTotal, addonManager,
                                 cb, closure);
     NS_ENSURE_SUCCESS(rv, rv);
 
     xpc::ZoneStatsExtras zExtrasTotal;
     zExtrasTotal.pathPrefix.AssignLiteral("js-main-runtime/zones/");
-    rv = ReportZoneStats(rtStats.zTotals, zExtrasTotal, cb, closure);
+    rv = ReportZoneStats(rtStats.zTotals, zExtrasTotal, cb, closure, anonymize);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Report the sum of the runtime/ numbers.
     REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"),
         KIND_OTHER, rtTotal,
         "The sum of all measurements under 'explicit/js-non-window/runtime/'.");
 
     // Report the numbers for memory outside of compartments.
@@ -2918,17 +2973,20 @@ AccumulateTelemetryCallback(int id, uint
     }
 }
 
 static void
 CompartmentNameCallback(JSRuntime *rt, JSCompartment *comp,
                         char *buf, size_t bufsize)
 {
     nsCString name;
-    GetCompartmentName(comp, name, false);
+    // This is called via the JSAPI and isn't involved in memory reporting, so
+    // we don't need to anonymize compartment names.
+    int anonymizeID = 0;
+    GetCompartmentName(comp, name, &anonymizeID, /* replaceSlashes = */ false);
     if (name.Length() >= bufsize)
         name.Truncate(bufsize - 1);
     memcpy(buf, name.get(), name.Length() + 1);
 }
 
 static bool
 PreserveWrapper(JSContext *cx, JSObject *obj)
 {
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -395,17 +395,19 @@ 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,
                                  nsIMemoryReporterCallback *cb,
-                                 nsISupports *closure, size_t *rtTotal = nullptr);
+                                 nsISupports *closure,
+                                 bool anonymize,
+                                 size_t *rtTotal = nullptr);
 
 /**
  * Throws an exception on cx and returns false.
  */
 bool
 Throw(JSContext *cx, nsresult rv);
 
 /**
--- a/layout/base/nsStyleSheetService.cpp
+++ b/layout/base/nsStyleSheetService.cpp
@@ -284,17 +284,17 @@ SizeOfElementIncludingThis(nsIStyleSheet
 {
   return aElement->SizeOfIncludingThis(aMallocSizeOf);
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(StyleSheetServiceMallocSizeOf)
 
 NS_IMETHODIMP
 nsStyleSheetService::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                    nsISupports* aData)
+                                    nsISupports* aData, bool aAnonymize)
 {
   return MOZ_COLLECT_REPORT(
     "explicit/layout/style-sheet-service", KIND_HEAP, UNITS_BYTES,
     SizeOfIncludingThis(StyleSheetServiceMallocSizeOf),
     "Memory used for style sheets held by the style sheet service.");
 }
 
 size_t
--- a/layout/style/nsLayoutStylesheetCache.cpp
+++ b/layout/style/nsLayoutStylesheetCache.cpp
@@ -243,17 +243,17 @@ nsLayoutStylesheetCache::Shutdown()
   NS_IF_RELEASE(gCSSLoader);
   NS_IF_RELEASE(gStyleCache);
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(LayoutStylesheetCacheMallocSizeOf)
 
 NS_IMETHODIMP
 nsLayoutStylesheetCache::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                        nsISupports* aData)
+                                        nsISupports* aData, bool aAnonymize)
 {
   return MOZ_COLLECT_REPORT(
     "explicit/layout/style-sheet-cache", KIND_HEAP, UNITS_BYTES,
     SizeOfIncludingThis(LayoutStylesheetCacheMallocSizeOf),
     "Memory used for some built-in style sheets.");
 }
 
 
--- a/mobile/android/chrome/content/MemoryObserver.js
+++ b/mobile/android/chrome/content/MemoryObserver.js
@@ -61,11 +61,12 @@ var MemoryObserver = {
 
   gc: function() {
     window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).garbageCollect();
     Cu.forceGC();
   },
 
   dumpMemoryStats: function(aLabel) {
     let memDumper = Cc["@mozilla.org/memory-info-dumper;1"].getService(Ci.nsIMemoryInfoDumper);
-    memDumper.dumpMemoryInfoToTempDir(aLabel, /* minimize = */ false);
+    memDumper.dumpMemoryInfoToTempDir(aLabel, /* anonymize = */ false,
+                                      /* minimize = */ false);
   },
 };
--- a/modules/libpref/src/Preferences.cpp
+++ b/modules/libpref/src/Preferences.cpp
@@ -288,17 +288,18 @@ PreferenceServiceReporter::CountReferent
 
   return PL_DHASH_NEXT;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(PreferenceServiceMallocSizeOf)
 
 NS_IMETHODIMP
 PreferenceServiceReporter::CollectReports(nsIMemoryReporterCallback* aCb,
-                                          nsISupports* aClosure)
+                                          nsISupports* aClosure,
+                                          bool aAnonymize)
 {
 #define REPORT(_path, _kind, _units, _amount, _desc)                          \
     do {                                                                      \
       nsresult rv;                                                            \
       rv = aCb->Callback(EmptyCString(), _path, _kind,                        \
                          _units, _amount, NS_LITERAL_CSTRING(_desc),          \
                          aClosure);                                           \
       NS_ENSURE_SUCCESS(rv, rv);                                              \
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -3263,17 +3263,17 @@ nsCacheService::LeavePrivateBrowsing()
         gService->mMemoryDevice->EvictPrivateEntries();
     }
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(DiskCacheDeviceMallocSizeOf)
 
 NS_IMETHODIMP
 nsCacheService::CollectReports(nsIHandleReportCallback* aHandleReport,
-                               nsISupports* aData)
+                               nsISupports* aData, bool aAnonymize)
 {
     size_t disk = 0;
     if (mDiskDevice) {
         nsCacheServiceAutoLock
             lock(LOCK_TELEM(NSCACHESERVICE_DISKDEVICEHEAPSIZE));
         disk = mDiskDevice->SizeOfIncludingThis(DiskCacheDeviceMallocSizeOf);
     }
 
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -1740,17 +1740,19 @@ PLDHashOperator ReportStorageMemory(cons
                                     void* aClosure)
 {
   CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
 
   size_t size = aTable->SizeOfIncludingThis(&CollectEntryMemory,
                                             CacheStorageService::MallocSizeOf,
                                             aTable);
 
-  ReportStorageMemoryData& data = *static_cast<ReportStorageMemoryData*>(aClosure);
+  ReportStorageMemoryData& data =
+    *static_cast<ReportStorageMemoryData*>(aClosure);
+  // These key names are not privacy-sensitive.
   data.mHandleReport->Callback(
     EmptyCString(),
     nsPrintfCString("explicit/network/cache2/%s-storage(%s)",
       aTable->Type() == CacheEntryTable::MEMORY_ONLY ? "memory" : "disk",
       aKey.BeginReading()),
     nsIMemoryReporter::KIND_HEAP,
     nsIMemoryReporter::UNITS_BYTES,
     size,
@@ -1758,17 +1760,18 @@ PLDHashOperator ReportStorageMemory(cons
     data.mData);
 
   return PL_DHASH_NEXT;
 }
 
 } // anon
 
 NS_IMETHODIMP
-CacheStorageService::CollectReports(nsIMemoryReporterCallback* aHandleReport, nsISupports* aData)
+CacheStorageService::CollectReports(nsIMemoryReporterCallback* aHandleReport,
+                                    nsISupports* aData, bool aAnonymize)
 {
   nsresult rv;
 
   rv = MOZ_COLLECT_REPORT(
     "explicit/network/cache2/io", KIND_HEAP, UNITS_BYTES,
     CacheFileIOManager::SizeOfIncludingThis(MallocSizeOf),
     "Memory used by the cache IO manager.");
   if (NS_WARN_IF(NS_FAILED(rv)))
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -4355,15 +4355,15 @@ nsCookieService::SizeOfIncludingThis(moz
 
   return n;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(CookieServiceMallocSizeOf)
 
 NS_IMETHODIMP
 nsCookieService::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                nsISupports* aData)
+                                nsISupports* aData, bool aAnonymize)
 {
   return MOZ_COLLECT_REPORT(
     "explicit/cookie-service", KIND_HEAP, UNITS_BYTES,
     SizeOfIncludingThis(CookieServiceMallocSizeOf),
     "Memory used by the cookie service.");
 }
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -958,16 +958,16 @@ nsDNSService::SizeOfIncludingThis(mozill
                                            mallocSizeOf);
     return n;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(DNSServiceMallocSizeOf)
 
 NS_IMETHODIMP
 nsDNSService::CollectReports(nsIHandleReportCallback* aHandleReport,
-                             nsISupports* aData)
+                             nsISupports* aData, bool aAnonymize)
 {
     return MOZ_COLLECT_REPORT(
         "explicit/network/dns-service", KIND_HEAP, UNITS_BYTES,
         SizeOfIncludingThis(DNSServiceMallocSizeOf),
         "Memory used for the DNS service.");
 }
 
--- a/netwerk/dns/nsEffectiveTLDService.cpp
+++ b/netwerk/dns/nsEffectiveTLDService.cpp
@@ -106,17 +106,17 @@ nsEffectiveTLDService::~nsEffectiveTLDSe
   UnregisterWeakMemoryReporter(this);
   gService = nullptr;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(EffectiveTLDServiceMallocSizeOf)
 
 NS_IMETHODIMP
 nsEffectiveTLDService::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                      nsISupports* aData)
+                                      nsISupports* aData, bool aAnonymize)
 {
   return MOZ_COLLECT_REPORT(
     "explicit/xpcom/effective-TLD-service", KIND_HEAP, UNITS_BYTES,
     SizeOfIncludingThis(EffectiveTLDServiceMallocSizeOf),
     "Memory used by the effective TLD service.");
 }
 
 size_t
--- a/netwerk/protocol/http/SpdyZlibReporter.cpp
+++ b/netwerk/protocol/http/SpdyZlibReporter.cpp
@@ -23,16 +23,17 @@ SpdyZlibReporter::Alloc(void*, uInt item
 /* static */ void
 SpdyZlibReporter::Free(void*, void* p)
 {
   sAmount -= MallocSizeOfOnFree(p);
   moz_free(p);
 }
 
 NS_IMETHODIMP
-SpdyZlibReporter::CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+SpdyZlibReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
+                                 nsISupports* aData, bool aAnonymize)
 {
   return MOZ_COLLECT_REPORT(
     "explicit/network/spdy-zlib-buffers", KIND_HEAP, UNITS_BYTES, sAmount,
     "Memory allocated for SPDY zlib send and receive buffers.");
 }
 
 } // namespace mozilla
--- a/netwerk/protocol/http/SpdyZlibReporter.h
+++ b/netwerk/protocol/http/SpdyZlibReporter.h
@@ -39,12 +39,13 @@ private:
   // must be thread-safe.
   static Atomic<size_t> sAmount;
 
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
   MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
   MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
 
   NS_IMETHODIMP
-  CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData);
+  CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+                 bool aAnonymize);
 };
 
 } // namespace mozilla
--- a/startupcache/StartupCache.cpp
+++ b/startupcache/StartupCache.cpp
@@ -51,17 +51,17 @@
 
 namespace mozilla {
 namespace scache {
 
 MOZ_DEFINE_MALLOC_SIZE_OF(StartupCacheMallocSizeOf)
 
 NS_IMETHODIMP
 StartupCache::CollectReports(nsIHandleReportCallback* aHandleReport,
-                             nsISupports* aData)
+                             nsISupports* aData, bool aAnonymize)
 {
 #define REPORT(_path, _kind, _amount, _desc)                                \
   do {                                                                      \
     nsresult rv =                                                           \
       aHandleReport->Callback(EmptyCString(),                               \
                               NS_LITERAL_CSTRING(_path),                    \
                               _kind, UNITS_BYTES, _amount,                  \
                               NS_LITERAL_CSTRING(_desc), aData);            \
--- a/storage/src/mozStorageService.cpp
+++ b/storage/src/mozStorageService.cpp
@@ -119,17 +119,17 @@ ReportConn(nsIHandleReportCallback *aHan
 // 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_IMETHODIMP
 Service::CollectReports(nsIHandleReportCallback *aHandleReport,
-                        nsISupports *aData)
+                        nsISupports *aData, bool aAnonymize)
 {
   nsresult rv;
   size_t totalConnSize = 0;
   {
     nsTArray<nsRefPtr<Connection> > connections;
     getConnections(connections);
 
     for (uint32_t i = 0; i < connections.Length(); i++) {
@@ -138,16 +138,17 @@ Service::CollectReports(nsIHandleReportC
       // Someone may have closed the Connection, in which case we skip it.
       bool isReady;
       (void)conn->GetConnectionReady(&isReady);
       if (!isReady) {
           continue;
       }
 
       nsCString pathHead("explicit/storage/sqlite/");
+      // This filename isn't privacy-sensitive, and so is never anonymized.
       pathHead.Append(conn->getFilename());
       pathHead.Append('/');
 
       SQLiteMutexAutoLock lockedScope(conn->sharedDBMutex);
 
       NS_NAMED_LITERAL_CSTRING(stmtDesc,
         "Memory (approximate) used by all prepared statements used by "
         "connections to this database.");
--- a/testing/mochitest/tests/SimpleTest/MemoryStats.js
+++ b/testing/mochitest/tests/SimpleTest/MemoryStats.js
@@ -85,17 +85,17 @@ MemoryStats.dump = function (dumpFn,
         var basename = "about-memory-" + testNumber + ".json.gz";
         var dumpfile = MemoryStats.constructPathname(dumpOutputDirectory,
                                                      basename);
         dumpFn("TEST-INFO | " + testURL + " | MEMDUMP-START " + dumpfile);
         var md = MemoryStats._getService("@mozilla.org/memory-info-dumper;1",
                                          "nsIMemoryInfoDumper");
         md.dumpMemoryReportsToNamedFile(dumpfile, function () {
             dumpFn("TEST-INFO | " + testURL + " | MEMDUMP-END");
-        }, null);
+        }, null, /* anonymize = */ false);
 
     }
 
     if (dumpDMD && typeof(DMDReportAndDump) != undefined) {
         var basename = "dmd-" + testNumber + ".txt";
         var dumpfile = MemoryStats.constructPathname(dumpOutputDirectory,
                                                      basename);
         dumpFn("TEST-INFO | " + testURL + " | DMD-DUMP " + dumpfile);
--- a/toolkit/components/aboutmemory/content/aboutMemory.js
+++ b/toolkit/components/aboutmemory/content/aboutMemory.js
@@ -21,16 +21,17 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const CC = Components.Constructor;
 
 const KIND_NONHEAP           = Ci.nsIMemoryReporter.KIND_NONHEAP;
 const KIND_HEAP              = Ci.nsIMemoryReporter.KIND_HEAP;
 const KIND_OTHER             = Ci.nsIMemoryReporter.KIND_OTHER;
+
 const UNITS_BYTES            = Ci.nsIMemoryReporter.UNITS_BYTES;
 const UNITS_COUNT            = Ci.nsIMemoryReporter.UNITS_COUNT;
 const UNITS_COUNT_CUMULATIVE = Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE;
 const UNITS_PERCENTAGE       = Ci.nsIMemoryReporter.UNITS_PERCENTAGE;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
@@ -126,16 +127,19 @@ function onUnload()
 let gMain;
 
 // The <div> holding the footer.
 let gFooter;
 
 // The "verbose" checkbox.
 let gVerbose;
 
+// The "anonymize" checkbox.
+let gAnonymize;
+
 // Values for the second argument to updateMainAndFooter.
 let HIDE_FOOTER = 0;
 let SHOW_FOOTER = 1;
 
 function updateMainAndFooter(aMsg, aFooterAction, aClassName)
 {
   // Clear gMain by replacing it with an empty node.
   let tmp = gMain.cloneNode(false);
@@ -298,40 +302,46 @@ function onLoad()
   const DMDDisabledDesc = "DMD is not running. Please re-start with $DMD and " +
                           "the other relevant environment variables set " +
                           "appropriately.";
 
   let ops = appendElement(header, "div", "");
 
   let row1 = appendElement(ops, "div", "opsRow");
 
-  let labelDiv =
+  let labelDiv1 =
    appendElementWithText(row1, "div", "opsRowLabel", "Show memory reports");
-  let label = appendElementWithText(labelDiv, "label", "");
-  gVerbose = appendElement(label, "input", "");
+  let label1 = appendElementWithText(labelDiv1, "label", "");
+  gVerbose = appendElement(label1, "input", "");
   gVerbose.type = "checkbox";
   gVerbose.id = "verbose";   // used for testing
-
-  appendTextNode(label, "verbose");
+  appendTextNode(label1, "verbose");
 
   const kEllipsis = "\u2026";
 
   // The "measureButton" id is used for testing.
   appendButton(row1, CuDesc, doMeasure, "Measure", "measureButton");
   appendButton(row1, LdDesc, () => fileInput1.click(), "Load" + kEllipsis);
   appendButton(row1, DfDesc, () => fileInput2.click(),
                "Load and diff" + kEllipsis);
   appendButton(row1, RdDesc, updateAboutMemoryFromClipboard,
                "Read from clipboard");
 
   let row2 = appendElement(ops, "div", "opsRow");
 
-  appendElementWithText(row2, "div", "opsRowLabel", "Save memory reports");
+  let labelDiv2 =
+    appendElementWithText(row2, "div", "opsRowLabel", "Save memory reports");
   appendButton(row2, SvDesc, saveReportsToFile, "Measure and save" + kEllipsis);
 
+  // XXX njn: still not happy with the placement of this checkbox
+  let label2 = appendElementWithText(labelDiv2, "label", "");
+  gAnonymize = appendElement(label2, "input", "");
+  gAnonymize.type = "checkbox";
+  appendTextNode(label2, "anonymize");
+
   let row3 = appendElement(ops, "div", "opsRow");
 
   appendElementWithText(row3, "div", "opsRowLabel", "Free memory");
   appendButton(row3, GCDesc, doGC,  "GC");
   appendButton(row3, CCDesc, doCC,  "CC");
   appendButton(row3, MMDesc, doMMU, "Minimize memory usage");
 
   let row4 = appendElement(ops, "div", "opsRow");
@@ -488,18 +498,18 @@ function updateAboutMemoryFromReporters(
                       aDescription, /* presence = */ undefined);
       }
 
       let displayReportsAndFooter = function() {
         updateMainAndFooter("", SHOW_FOOTER);
         aDisplayReports();
       }
 
-      gMgr.getReports(handleReport, null,
-                      displayReportsAndFooter, null);
+      gMgr.getReports(handleReport, null, displayReportsAndFooter, null,
+                      gAnonymize.checked);
     }
 
     // Process the reports from the live memory reporters.
     appendAboutMemoryMain(processLiveMemoryReports,
                           gMgr.hasMozMallocUsableSize);
 
   } catch (ex) {
     handleException(ex);
@@ -1957,17 +1967,18 @@ function saveReportsToFile()
   let fpFinish = function(file) {
     let dumper = Cc["@mozilla.org/memory-info-dumper;1"]
                    .getService(Ci.nsIMemoryInfoDumper);
 
     let finishDumping = () => {
       updateMainAndFooter("Saved reports to " + file.path, HIDE_FOOTER);
     }
 
-    dumper.dumpMemoryReportsToNamedFile(file.path, finishDumping, null);
+    dumper.dumpMemoryReportsToNamedFile(file.path, finishDumping, null,
+                                        gAnonymize.checked);
   }
 
   let fpCallback = function(aResult) {
     if (aResult == Ci.nsIFilePicker.returnOK ||
         aResult == Ci.nsIFilePicker.returnReplace) {
       fpFinish(fp.file);
     }
   };
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul
@@ -38,17 +38,17 @@
   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;
 
   let fakeReporters = [
-    { collectReports: function(aCbObj, aClosure) {
+    { collectReports: function(aCbObj, aClosure, aAnonymize) {
         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);
@@ -75,43 +75,43 @@
         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);
       }
     },
-    { collectReports: function(aCbObj, aClosure) {
+    { collectReports: function(aCbObj, aClosure, aAnonymize) {
         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/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);
       }
     },
-    { collectReports: function(aCbObj, aClosure) {
+    { collectReports: function(aCbObj, aClosure, aAnonymize) {
         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);
         f("compartments/user/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", OTHER, COUNT, 1);
       }
     },
-    { collectReports: function(aCbObj, aClosure) {
+    { collectReports: function(aCbObj, aClosure, aAnonymize) {
         function f(aP) {
           aCbObj.callback("", aP, OTHER, COUNT, 1, "Desc.", aClosure);
         }
         f("compartments/user/bar");
         f("compartments/system/bar");
       }
     }
   ];
@@ -134,17 +134,17 @@
     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 = [
-    { collectReports: function(aCbObj, aClosure) {
+    { collectReports: function(aCbObj, aClosure, aAnonymize) {
         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
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory2.xul
@@ -33,17 +33,17 @@
   const BYTES = Ci.nsIMemoryReporter.UNITS_BYTES;
 
   let hiPath  = "explicit/h/i";
   let hi2Path = "explicit/h/i2";
   let jkPath  = "explicit/j/k";
   let jk2Path = "explicit/j/k2";
 
   let fakeReporters = [
-    { collectReports: function(aCbObj, aClosure) {
+    { collectReports: function(aCbObj, aClosure, aAnonymize) {
         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);
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul
@@ -28,17 +28,17 @@
   // 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;
 
   let fakeReporters = [
-    { collectReports: function(aCbObj, aClosure) {
+    { collectReports: function(aCbObj, aClosure, aAnonymize) {
         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.");
       }
@@ -135,17 +135,18 @@
 
         fileInput1.dispatchEvent(e);
         check();
       }
 
       if (aDumpFirst) {
         let dumper = Cc["@mozilla.org/memory-info-dumper;1"].
                         getService(Ci.nsIMemoryInfoDumper);
-        dumper.dumpMemoryReportsToNamedFile(filePath, loadAndCheck, null);
+        dumper.dumpMemoryReportsToNamedFile(filePath, loadAndCheck, null,
+                                            /* anonymize = */ false);
       } else {
         loadAndCheck();
       }
 
     } else {
       let fileInput2 =
         frame.contentWindow.document.getElementById("fileInput2");
       fileInput2.value = filePath;    // this works because it's a chrome test
--- a/toolkit/components/aboutmemory/tests/test_aboutmemory5.xul
+++ b/toolkit/components/aboutmemory/tests/test_aboutmemory5.xul
@@ -80,17 +80,18 @@
 
     let filePath = getFilePath("memory-reports-dumped.json.gz");
 
     let e = document.createEvent('Event');
     e.initEvent('change', true, true);
 
     let dumper = Cc["@mozilla.org/memory-info-dumper;1"].
                     getService(Ci.nsIMemoryInfoDumper);
-    dumper.dumpMemoryReportsToNamedFile(filePath, loadAndCheck, null);
+    dumper.dumpMemoryReportsToNamedFile(filePath, loadAndCheck, null,
+                                        /* anonymize = */ false);
 
     function loadAndCheck() {
       // Load the file.
       let fileInput1 =
         frame.contentWindow.document.getElementById("fileInput1");
       fileInput1.value = filePath;    // this works because it's a chrome test
       fileInput1.dispatchEvent(e);
 
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
@@ -11,16 +11,20 @@
        presentation of memory reports in about:memory. -->
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <!-- In bug 773533, <marquee> elements crashed the JS memory reporter -->
   <marquee>Marquee</marquee>
   </body>
 
+  <!-- some URIs that should be anonymized in anonymous mode -->
+  <iframe id="amFrame"  height="200" src="http://example.org:80"></iframe>
+  <iframe id="amFrame"  height="200" src="https://example.com:443"></iframe>
+
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
 
   // Nb: this test is all JS and so should be done with an xpcshell test,
   // but bug 671753 is preventing the memory-reporter-manager from being
   // accessed from xpcshell.
 
@@ -62,17 +66,18 @@
   for (let i = 0; i < 10000; i++) {
     let str = (Math.random() > 0.5 ? "!" : "@") + ")(*&";
     shortStrings.push(str);
   }
 
   let mySandbox = Components.utils.Sandbox(document.nodePrincipal,
                     { sandboxName: "this-is-a-sandbox-name" });
 
-  function handleReport(aProcess, aPath, aKind, aUnits, aAmount, aDescription)
+  function handleReportNormal(aProcess, aPath, aKind, aUnits, aAmount,
+                              aDescription)
   {
     // Record the values of some notable reporters.
     if (aPath === "vsize") {
       vsizeAmounts.push(aAmount);
     } else if (aPath === "resident") {
       residentAmounts.push(aAmount);
     } else if (aPath === "js-main-runtime-gc-heap-committed/used/gc-things") {
       jsGcHeapAmounts.push(aAmount); 
@@ -100,16 +105,35 @@
       present.sandboxLocation = true;
     } else if (aPath.contains(bigStringPrefix)) {
       present.bigString = true;
     } else if (aPath.contains("!)(*&")) {
       present.smallString1 = true;
     } else if (aPath.contains("@)(*&")) {
       present.smallString2 = true;
     }
+
+    // Shouldn't get any anonymized paths.
+    if (aPath.contains('<anonymized')) {
+        present.anonymizedWhenUnnecessary = true;
+    }
+  }
+
+  function handleReportAnonymized(aProcess, aPath, aKind, aUnits, aAmount,
+                                  aDescription)
+  {
+    // Shouldn't get http: or https: in any paths.
+    if (aPath.contains('http:')) {
+        present.httpWhenAnonymized = true;
+    }
+
+    // file: URLs should have their path anonymized.
+    if (aPath.search('file:..[^<]') !== -1) {
+        present.unanonymizedFilePathWhenAnonymized = true;
+    }
   }
 
   let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
             getService(Ci.nsIMemoryReporterManager);
 
   // Access the distinguished amounts (mgr.explicit et al.) just to make sure
   // they don't crash.  We can't check their actual values because they're
   // non-deterministic.
@@ -166,17 +190,21 @@
   let otherSize = {};
   let totalSize = {};
   let jsMilliseconds = {};
   let nonJSMilliseconds = {};
   mgr.sizeOfTab(window, jsObjectsSize, jsStringsSize, jsOtherSize,
                 domSize, styleSize, otherSize, totalSize,
                 jsMilliseconds, nonJSMilliseconds);
 
-  mgr.getReportsForThisProcess(handleReport, null);
+  mgr.getReportsForThisProcess(handleReportNormal, null,
+                               /* anonymize = */ false);
+
+  mgr.getReportsForThisProcess(handleReportAnonymized, null,
+                               /* anonymize = */ true);
 
   function checkSpecialReport(aName, aAmounts, aCanBeUnreasonable)
   {
     ok(aAmounts.length == 1, aName + " has " + aAmounts.length + " report");
     let n = aAmounts[0];
     // Check the size is reasonable -- i.e. not ridiculously large or small.
     ok((100 * 1000 <= n && n <= 10 * 1000 * 1000 * 1000) || aCanBeUnreasonable,
        aName + "'s size is reasonable");
@@ -197,16 +225,20 @@
   ok(present.images,                      "images is present");
   ok(present.xptiWorkingSet,              "xpti-working-set is present");
   ok(present.atomTable,                   "atom-table is present");
   ok(present.sandboxLocation,             "sandbox locations are present");
   ok(present.bigString,                   "large string is present");
   ok(present.smallString1,                "small string 1 is present");
   ok(present.smallString2,                "small string 2 is present");
 
+  ok(!present.anonymizedWhenUnnecessary,  "anonymized paths are not present when unnecessary");
+  ok(!present.httpWhenAnonymized,         "http URLs are anonymized when necessary");
+  ok(!present.unanonymizedFilePathWhenAnonymized,
+                                          "file URLs are anonymized when necessary");
 
   // Reporter registration tests
 
   // collectReports() calls to the test reporter.
   let called = 0;
 
   // The test memory reporter, testing the various report units.
   // Also acts as a report collector, verifying the reported values match the
@@ -240,17 +272,17 @@
         expected: () => called
       },
       "test-memory-reporter-percentage": {
         units: PERCENTAGE,
         amount: () => 9999
       }
     },
     // nsIMemoryReporter
-    collectReports: function(callback, data) {
+    collectReports: function(callback, data, anonymize) {
       for (let path of Object.keys(this.tests)) {
         try {
           let test = this.tests[path];
           callback.callback(
             "", // Process. Should be "" initially.
             path,
             OTHER,
             test.units,
@@ -289,25 +321,27 @@
 
   // General memory reporter + registerStrongReporter tests.
   function test_register_strong() {
     let reporterAndCallback = new MemoryReporterAndCallback();
     // Registration works.
     mgr.registerStrongReporter(reporterAndCallback);
 
     // Check the generated reports.
-    mgr.getReportsForThisProcess(reporterAndCallback, null);
+    mgr.getReportsForThisProcess(reporterAndCallback, null,
+                                 /* anonymize = */ false);
     reporterAndCallback.finish();
 
     // Unregistration works.
     mgr.unregisterStrongReporter(reporterAndCallback);
 
     // The reporter was unregistered, hence there shouldn't be any reports from
     // the test reporter.
-    mgr.getReportsForThisProcess(reporterAndCallback, null);
+    mgr.getReportsForThisProcess(reporterAndCallback, null,
+                                 /* anonymize = */ false);
     reporterAndCallback.finish(0);
   }
 
   test_register_strong();
 
   // Check strong reporters a second time, to make sure a reporter can be
   // re-registered.
   test_register_strong();
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters2.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters2.xul
@@ -62,17 +62,17 @@
     }
 
     let processReports = function() {
       // First, test a failure case:  calling getReports() before the previous
       // getReports() has finished should silently abort.  (And the arguments
       // won't be used.)
       mgr.getReports(
         () => ok(false, "handleReport called for nested getReports() call"),
-        null, null, null
+        null, null, null, /* anonymize = */ false
       );
 
       // Close the remote processes.
       for (let i = 0; i < numRemotes; i++) {
         remotes[i].close();
       }
 
       // Check the results.
@@ -92,13 +92,14 @@
       }
       ok(numEmptyProcesses == 1, "correct empty process name count");
       ok(numNonEmptyProcesses == numRemotes,
                                  "correct non-empty process name count");
 
       SimpleTest.finish();
     }
 
-    mgr.getReports(handleReport, null, processReports, null);
+    mgr.getReports(handleReport, null, processReports, null,
+                   /* anonymize = */ false);
   }
 
   ]]></script>
 </window>
--- a/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul
+++ b/toolkit/components/aboutmemory/tests/test_sqliteMultiReporter.xul
@@ -35,17 +35,17 @@
                 getService(Ci.mozIStorageService);
   let db = storage.openDatabase(file);
   db.close();
 
   // 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);
-  mgr.getReportsForThisProcess(function(){}, null);
+  mgr.getReportsForThisProcess(function(){}, null, /* anonymize = */ false);
 
   // If we haven't crashed, we've passed, but the test harness requires that
   // we explicitly check something.
   ok(true, "didn't crash");
 
   ]]>
   </script>
 </window>
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -2229,17 +2229,17 @@ History::SizeOfEntryExcludingThis(KeyCla
 {
   return aEntry->array.SizeOfExcludingThis(aMallocSizeOf);
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(HistoryMallocSizeOf)
 
 NS_IMETHODIMP
 History::CollectReports(nsIHandleReportCallback* aHandleReport,
-                        nsISupports* aData)
+                        nsISupports* aData, bool aAnonymize)
 {
   return MOZ_COLLECT_REPORT(
     "explicit/history-links-hashtable", KIND_HEAP, UNITS_BYTES,
     SizeOfIncludingThis(HistoryMallocSizeOf),
     "Memory used by the hashtable that records changes to the visited state "
     "of links.");
 }
 
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -659,17 +659,17 @@ private:
 };
 
 TelemetryImpl*  TelemetryImpl::sTelemetry = nullptr;
 
 MOZ_DEFINE_MALLOC_SIZE_OF(TelemetryMallocSizeOf)
 
 NS_IMETHODIMP
 TelemetryImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
-                              nsISupports* aData)
+                              nsISupports* aData, bool aAnonymize)
 {
   return MOZ_COLLECT_REPORT(
     "explicit/telemetry", KIND_HEAP, UNITS_BYTES,
     SizeOfIncludingThis(TelemetryMallocSizeOf),
     "Memory used by the telemetry system.");
 }
 
 // A initializer to initialize histogram collection
--- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
@@ -244,17 +244,17 @@ nsUrlClassifierPrefixSet::Contains(uint3
 
   return NS_OK;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(UrlClassifierMallocSizeOf)
 
 NS_IMETHODIMP
 nsUrlClassifierPrefixSet::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                         nsISupports* aData)
+                                         nsISupports* aData, bool aAnonymize)
 {
   return aHandleReport->Callback(
     EmptyCString(), mMemoryReportPath, KIND_HEAP, UNITS_BYTES,
     SizeOfIncludingThis(UrlClassifierMallocSizeOf),
     NS_LITERAL_CSTRING("Memory used by the prefix set for a URL classifier."),
     aData);
 }
 
--- a/xpcom/base/AvailableMemoryTracker.cpp
+++ b/xpcom/base/AvailableMemoryTracker.cpp
@@ -339,17 +339,17 @@ LowMemoryEventsPhysicalDistinguishedAmou
 }
 
 class LowEventsReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData)
+                            nsISupports* aData, bool aAnonymize)
   {
     nsresult rv;
 
     // We only do virtual-memory tracking on 32-bit builds.
     if (sizeof(void*) == 4) {
       rv = MOZ_COLLECT_REPORT(
         "low-memory-events/virtual", KIND_OTHER, UNITS_COUNT_CUMULATIVE,
         LowMemoryEventsVirtualDistinguishedAmount(),
--- a/xpcom/base/SystemMemoryReporter.cpp
+++ b/xpcom/base/SystemMemoryReporter.cpp
@@ -131,18 +131,23 @@ public:
       }                                                                       \
     }                                                                         \
   } while (0)
 
 #define REPORT(_path, _amount, _desc) \
   REPORT_WITH_CLEANUP(_path, UNITS_BYTES, _amount, _desc, (void)0)
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData)
+                            nsISupports* aData, bool aAnonymize)
   {
+    // There is lots of privacy-sensitive data in /proc. Just skip this
+    // reporter entirely when anonymization is required.
+    if (aAnonymize)
+      return NS_OK;
+
     if (!Preferences::GetBool("memory.system_memory_reporter")) {
       return NS_OK;
     }
 
     // Read relevant fields from /proc/meminfo.
     int64_t memTotal = 0, memFree = 0;
     nsresult rv = ReadMemInfo(&memTotal, &memFree);
 
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -3264,17 +3264,17 @@ nsCycleCollector::CollectWhite()
 ////////////////////////
 // Memory reporting
 ////////////////////////
 
 MOZ_DEFINE_MALLOC_SIZE_OF(CycleCollectorMallocSizeOf)
 
 NS_IMETHODIMP
 nsCycleCollector::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                 nsISupports* aData)
+                                 nsISupports* aData, bool aAnonymize)
 {
   size_t objectSize, graphNodesSize, graphEdgesSize, weakMapsSize,
          purpleBufferSize;
   SizeOfIncludingThis(CycleCollectorMallocSizeOf,
                       &objectSize,
                       &graphNodesSize, &graphEdgesSize,
                       &weakMapsSize,
                       &purpleBufferSize);
--- a/xpcom/base/nsIMemoryInfoDumper.idl
+++ b/xpcom/base/nsIMemoryInfoDumper.idl
@@ -39,25 +39,31 @@ interface nsIDumpGCAndCCLogsCallback : n
               in bool aIsParent);
 
   /**
    * Called when GC/CC logging has finished, after all calls to |onDump|.
    */
   void onFinish();
 };
 
-[scriptable, builtinclass, uuid(a156fda4-aea0-4da2-9daa-9858430fade9)]
+[scriptable, builtinclass, uuid(48541b74-47ee-4a62-9557-7f4b809bda5c)]
 interface nsIMemoryInfoDumper : nsISupports
 {
   /**
    * This dumps gzipped memory reports for this process and its child
    * processes.  If a file of the given name exists, it will be overwritten.
    *
    * @param aFilename The output file.
    *
+   * @param aFinishDumping The callback called on completion.
+   *
+   * @param aFinishDumpingData The environment for the callback.
+   *
+   * @param aAnonymize Should the reports be anonymized?
+   *
    * Sample output:
    *
    * {
    *   "hasMozMallocUsableSize":true,
    *   "reports": [
    *     {"process":"Main Process (pid 12345)", "path":"explicit/foo/bar",
    *      "kind":1, "units":0, "amount":2000000, "description":"Foo bar."},
    *     {"process":"Main Process (pid 12345)", "path":"heap-allocated",
@@ -118,17 +124,18 @@ interface nsIMemoryInfoDumper : nsISuppo
    *         }
    *       }
    *     }
    *   }
    * }
    */
   void dumpMemoryReportsToNamedFile(in AString aFilename,
                                     in nsIFinishDumpingCallback aFinishDumping,
-                                    in nsISupports aFinishDumpingData);
+                                    in nsISupports aFinishDumpingData,
+                                    in boolean aAnonymize);
 
   /**
    * Similar to dumpMemoryReportsToNamedFile, this method dumps gzipped memory
    * reports for this process and its child processes to files in the tmp
    * directory called memory-reports-<identifier>-<pid>.json.gz (or something
    * similar, such as memory-reports-<identifier>-<pid>-1.json.gz; no existing
    * file will be overwritten).
    *
@@ -139,23 +146,26 @@ interface nsIMemoryInfoDumper : nsISuppo
    * @param aIdentifier this identifier will appear in the filename of our
    *   about:memory dump and those of our children.
    *
    *   If the identifier is empty, the implementation may set it arbitrarily
    *   and use that new value for its own dump and the dumps of its child
    *   processes.  For example, the implementation may set |aIdentifier| to the
    *   number of seconds since the epoch.
    *
+   * @param aAnonymize Should the reports be anonymized?
+   *
    * @param aMinimizeMemoryUsage indicates whether we should run a series of
    *   gc/cc's in an attempt to reduce our memory usage before collecting our
    *   memory report.
    */
   void dumpMemoryInfoToTempDir(
     in AString aIdentifier,
-    in bool aMinimizeMemoryUsage);
+    in boolean aAnonymize,
+    in boolean aMinimizeMemoryUsage);
 
   /**
    * Dump GC and CC logs to files in the OS's temp directory (or in
    * $MOZ_CC_LOG_DIRECTORY, if that environment variable is specified).
    *
    * @param aIdentifier If aIdentifier is non-empty, this string will appear in
    *   the filenames of the logs we create (both for this process and, if
    *   aDumpChildProcesses is true, for our child processes).
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -142,34 +142,57 @@ interface nsIMemoryReporterCallback : ns
  *   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.
  *
  * It is preferred, but not required, that report descriptions use complete
  * sentences (i.e. start with a capital letter and end with a period, or
  * similar).
  */
-[scriptable, uuid(0884cd0f-5829-4381-979b-0f53904030ed)]
+[scriptable, uuid(92a36db1-46bd-4fe6-988e-47db47236d8b)]
 interface nsIMemoryReporter : nsISupports
 {
   /*
    * Run the reporter.
+   *
+   * If |anonymize| is true, the memory reporter should anonymize any
+   * privacy-sensitive details in memory report paths, by replacing them with a
+   * string such as "<anonymized>". Anonymized memory reports may be sent
+   * automatically via crash reports or telemetry.
+   *
+   * The following things are considered privacy-sensitive.
+   *
+   * - Content domains and URLs, and information derived from them.
+   * - Content data, such as strings.
+   * - Details about content code, such as filenames, function names or stack
+   *   traces.
+   * - Details about or data from the user's system, such as filenames.
+   * - Running apps.
+   *
+   * In short, anything that could identify parts of the user's browsing
+   * history is considered privacy-sensitive.
+   *
+   * The following thing are not considered privacy-sensitive.
+   *
+   * - Chrome domains and URLs.
+   * - Information about installed extensions.
    */
   void collectReports(in nsIMemoryReporterCallback callback,
-                      in nsISupports data);
+                      in nsISupports data,
+                      in boolean anonymize);
 
   /*
-   * Kinds.  See the |kind| comment in nsIMemoryReporterCallback.
+   * Kinds. See the |kind| comment in nsIMemoryReporterCallback.
    */
   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.
+   * 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, function, uuid(548b3909-c04d-4ca6-8466-b8bee3837457)]
@@ -231,52 +254,59 @@ interface nsIMemoryReporterManager : nsI
    *
    * |finishReporting| is called even if, for example, some child processes
    * fail to report back.  However, calls to this method will silently and
    * immediately abort -- and |finishReporting| will not be called -- if a
    * previous getReports() call is still in flight, i.e. if it has not yet
    * finished invoking |finishReporting|.  The silent abort is because the
    * in-flight request will finish soon, and the caller would very likely just
    * catch and ignore any error anyway.
+   *
+   * If |anonymize| is true, it indicates that the memory reporters should
+   * anonymize any privacy-sensitive data (see above).
    */
   void getReports(in nsIMemoryReporterCallback handleReport,
                   in nsISupports handleReportData,
                   in nsIFinishReportingCallback finishReporting,
-                  in nsISupports finishReportingData);
+                  in nsISupports finishReportingData,
+                  in boolean anonymize);
 
   /*
    * As above, but: If |minimizeMemoryUsage| is true, then each process will
    * minimize its memory usage (see the |minimizeMemoryUsage| method) before
    * gathering its report.  If DMD is enabled and |DMDDumpIdent| is non-empty
    * then write a DMD report to a file in the usual temporary directory (see
    * |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.)
    */
   [noscript] void
     getReportsExtended(in nsIMemoryReporterCallback handleReport,
                        in nsISupports handleReportData,
                        in nsIFinishReportingCallback finishReporting,
                        in nsISupports finishReportingData,
+                       in boolean anonymize,
                        in boolean minimizeMemoryUsage,
                        in AString DMDDumpIdent);
 
   /*
    * Get memory reports in the current process only.  |handleReport| is called
    * for each report.
    */
   void getReportsForThisProcess(in nsIMemoryReporterCallback handleReport,
-                                in nsISupports handleReportData);
+                                in nsISupports handleReportData,
+                                in boolean anonymize);
 
   /*
    * As above, but if DMD is enabled and |DMDDumpIdent| is non-empty
    * then write a DMD report to a file in the usual temporary directory (see
    * |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.)
    */
   [noscript] void
     getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
                                      in nsISupports handleReportData,
+                                     in boolean anonymize,
                                      in AString DMDDumpIdent);
 
   /*
    * The memory reporter manager, for the most part, treats reporters
    * registered with it as a black box.  However, there are some
    * "distinguished" amounts (as could be reported by a memory reporter) that
    * the manager provides as attributes, because they are sufficiently
    * interesting that we want external code (e.g. telemetry) to be able to rely
@@ -547,19 +577,20 @@ namespace mozilla {
 // be preferred to manually counting with MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC
 // and MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE.  The typical use is in a memory
 // reporter for a particular third party library:
 //
 //   class MyMemoryReporter : public CountingAllocatorBase<MyMemoryReporter>
 //   {
 //     ...
 //     NS_IMETHODIMP
-//     CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+//     CollectReports(nsIHandleReportCallback* aHandleReport,
+//                    nsISupports* aData, bool aAnonymize)
 //     {
-//        return MOZ_COLLECT_REPORTER(
+//        return MOZ_COLLECT_REPORT(
 //          "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES,
 //          MemoryAllocated(),
 //          "A description of what we are reporting."
 //     }
 //   };
 //
 //   ...somewhere later in the code...
 //   SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc,
--- a/xpcom/base/nsMemoryInfoDumper.cpp
+++ b/xpcom/base/nsMemoryInfoDumper.cpp
@@ -51,32 +51,35 @@ using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace {
 
 class DumpMemoryInfoToTempDirRunnable : public nsRunnable
 {
 public:
   DumpMemoryInfoToTempDirRunnable(const nsAString& aIdentifier,
-                                  bool aMinimizeMemoryUsage)
+                                  bool aAnonymize, bool aMinimizeMemoryUsage)
     : mIdentifier(aIdentifier)
+    , mAnonymize(aAnonymize)
     , mMinimizeMemoryUsage(aMinimizeMemoryUsage)
   {
   }
 
   NS_IMETHOD Run()
   {
     nsCOMPtr<nsIMemoryInfoDumper> dumper =
       do_GetService("@mozilla.org/memory-info-dumper;1");
-    dumper->DumpMemoryInfoToTempDir(mIdentifier, mMinimizeMemoryUsage);
+    dumper->DumpMemoryInfoToTempDir(mIdentifier, mAnonymize,
+                                    mMinimizeMemoryUsage);
     return NS_OK;
   }
 
 private:
   const nsString mIdentifier;
+  const bool mAnonymize;
   const bool mMinimizeMemoryUsage;
 };
 
 class GCAndCCLogDumpRunnable MOZ_FINAL
   : public nsRunnable
   , public nsIDumpGCAndCCLogsCallback
 {
 public:
@@ -152,21 +155,22 @@ namespace {
 // constant, so these have to be set at runtime.
 static uint8_t sDumpAboutMemorySignum;         // SIGRTMIN
 static uint8_t sDumpAboutMemoryAfterMMUSignum; // SIGRTMIN + 1
 static uint8_t sGCAndCCDumpSignum;             // SIGRTMIN + 2
 
 void doMemoryReport(const uint8_t aRecvSig)
 {
   // Dump our memory reports (but run this on the main thread!).
-  bool doMMUFirst = aRecvSig == sDumpAboutMemoryAfterMMUSignum;
+  bool minimize = aRecvSig == sDumpAboutMemoryAfterMMUSignum;
   LOG("SignalWatcher(sig %d) dispatching memory report runnable.", aRecvSig);
   nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
     new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
-                                        doMMUFirst);
+                                        /* anonymize = */ false,
+                                        minimize);
   NS_DispatchToMainThread(runnable);
 }
 
 void doGCCCDump(const uint8_t aRecvSig)
 {
   LOG("SignalWatcher(sig %d) dispatching GC/CC log runnable.", aRecvSig);
   // Dump GC and CC logs (from the main thread).
   nsRefPtr<GCAndCCLogDumpRunnable> runnable =
@@ -181,21 +185,22 @@ void doGCCCDump(const uint8_t aRecvSig)
 #endif // MOZ_SUPPORTS_RT_SIGNALS }
 
 #if defined(MOZ_SUPPORTS_FIFO) // {
 namespace {
 
 void
 doMemoryReport(const nsCString& aInputStr)
 {
-  bool doMMUMemoryReport = aInputStr.EqualsLiteral("minimize memory report");
+  bool minimize = aInputStr.EqualsLiteral("minimize memory report");
   LOG("FifoWatcher(command:%s) dispatching memory report runnable.", aInputStr.get());
   nsRefPtr<DumpMemoryInfoToTempDirRunnable> runnable =
     new DumpMemoryInfoToTempDirRunnable(/* identifier = */ EmptyString(),
-                                        doMMUMemoryReport);
+                                        /* anonymize = */ false,
+                                        minimize);
   NS_DispatchToMainThread(runnable);
 }
 
 void
 doGCCCDump(const nsCString& aInputStr)
 {
   bool doAllTracesGCCCDump = aInputStr.EqualsLiteral("gc log");
   LOG("FifoWatcher(command:%s) dispatching GC/CC log runnable.", aInputStr.get());
@@ -588,16 +593,17 @@ private:
   nsCString mrFilename;
   nsString mIdentifier;
 };
 
 NS_IMPL_ISUPPORTS(TempDirMemoryFinishCallback, nsIFinishReportingCallback)
 
 NS_IMETHODIMP
 nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
+                                            bool aAnonymize,
                                             bool aMinimizeMemoryUsage)
 {
   nsString identifier(aIdentifier);
   EnsureNonEmptyIdentifier(identifier);
 
 #ifdef MOZ_DMD
   // Clear DMD's reportedness state before running the memory reporters, to
   // avoid spurious twice-reported warnings.
@@ -653,16 +659,17 @@ nsMemoryInfoDumper::DumpMemoryInfoToTemp
   // Process reporters.
   nsCOMPtr<nsIMemoryReporterManager> mgr =
     do_GetService("@mozilla.org/memory-reporter-manager;1");
   nsRefPtr<DumpReportCallback> dumpReport = new DumpReportCallback(mrWriter);
   nsRefPtr<nsIFinishReportingCallback> finishReport =
     new TempDirMemoryFinishCallback(mrWriter, mrTmpFile, mrFilename, identifier);
   rv = mgr->GetReportsExtended(dumpReport, nullptr,
                                finishReport, nullptr,
+                               aAnonymize,
                                aMinimizeMemoryUsage,
                                identifier);
   return rv;
 }
 
 #ifdef MOZ_DMD
 nsresult
 nsMemoryInfoDumper::DumpDMD(const nsAString& aIdentifier)
@@ -821,17 +828,18 @@ private:
 };
 
 NS_IMPL_ISUPPORTS(FinishReportingCallback, nsIFinishReportingCallback)
 
 NS_IMETHODIMP
 nsMemoryInfoDumper::DumpMemoryReportsToNamedFile(
   const nsAString& aFilename,
   nsIFinishDumpingCallback* aFinishDumping,
-  nsISupports* aFinishDumpingData)
+  nsISupports* aFinishDumpingData,
+  bool aAnonymize)
 {
   MOZ_ASSERT(!aFilename.IsEmpty());
 
   // Create the file.
 
   nsCOMPtr<nsIFile> mrFile;
   nsresult rv = NS_NewLocalFile(aFilename, false, getter_AddRefs(mrFile));
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -870,12 +878,13 @@ nsMemoryInfoDumper::DumpMemoryReportsToN
   }
 
   // Process reports and finish up.
   nsRefPtr<DumpReportCallback> dumpReport = new DumpReportCallback(mrWriter);
   nsRefPtr<FinishReportingCallback> finishReporting =
     new FinishReportingCallback(aFinishDumping, aFinishDumpingData);
   nsCOMPtr<nsIMemoryReporterManager> mgr =
     do_GetService("@mozilla.org/memory-reporter-manager;1");
-  return mgr->GetReports(dumpReport, nullptr, finishReporting, mrWriter);
+  return mgr->GetReports(dumpReport, nullptr, finishReporting, mrWriter,
+                         aAnonymize);
 }
 
 #undef DUMP
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -117,17 +117,17 @@ ResidentUniqueDistinguishedAmount(int64_
 }
 
 class ResidentUniqueReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                           nsISupports* aData)
+                           nsISupports* aData, bool aAnonymize)
   {
     int64_t amount = 0;
     nsresult rv = ResidentUniqueDistinguishedAmount(&amount);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return MOZ_COLLECT_REPORT(
       "resident-unique", KIND_OTHER, UNITS_BYTES, amount,
 "Memory mapped by the process that is present in physical memory and not "
@@ -514,17 +514,17 @@ PrivateDistinguishedAmount(int64_t* aN)
 
 #ifdef HAVE_VSIZE_MAX_CONTIGUOUS_REPORTER
 class VsizeMaxContiguousReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                           nsISupports* aData)
+                           nsISupports* aData, bool aAnonymize)
   {
     int64_t amount;
     nsresult rv = VsizeMaxContiguousDistinguishedAmount(&amount);
     NS_ENSURE_SUCCESS(rv, rv);
     return MOZ_COLLECT_REPORT(
       "vsize-max-contiguous", KIND_OTHER, UNITS_BYTES, amount,
       "Size of the maximum contiguous block of available virtual "
       "memory.");
@@ -535,17 +535,17 @@ NS_IMPL_ISUPPORTS(VsizeMaxContiguousRepo
 
 #ifdef HAVE_PRIVATE_REPORTER
 class PrivateReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                           nsISupports* aData)
+                           nsISupports* aData, bool aAnonymize)
   {
     int64_t amount;
     nsresult rv = PrivateDistinguishedAmount(&amount);
     NS_ENSURE_SUCCESS(rv, rv);
     return MOZ_COLLECT_REPORT(
       "private", KIND_OTHER, UNITS_BYTES, amount,
 "Memory that cannot be shared with other processes, including memory that is "
 "committed and marked MEM_PRIVATE, data that is not mapped, and executable "
@@ -557,17 +557,17 @@ NS_IMPL_ISUPPORTS(PrivateReporter, nsIMe
 
 #ifdef HAVE_VSIZE_AND_RESIDENT_REPORTERS
 class VsizeReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                           nsISupports* aData)
+                           nsISupports* aData, bool aAnonymize)
   {
     int64_t amount;
     nsresult rv = VsizeDistinguishedAmount(&amount);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return MOZ_COLLECT_REPORT(
       "vsize", KIND_OTHER, UNITS_BYTES, amount,
 "Memory mapped by the process, including code and data segments, the heap, "
@@ -582,17 +582,17 @@ public:
 NS_IMPL_ISUPPORTS(VsizeReporter, nsIMemoryReporter)
 
 class ResidentReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                           nsISupports* aData)
+                           nsISupports* aData, bool aAnonymize)
   {
     int64_t amount;
     nsresult rv = ResidentDistinguishedAmount(&amount);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return MOZ_COLLECT_REPORT(
       "resident", KIND_OTHER, UNITS_BYTES, amount,
 "Memory mapped by the process that is present in physical memory, also known "
@@ -614,17 +614,17 @@ NS_IMPL_ISUPPORTS(ResidentReporter, nsIM
 #define HAVE_PAGE_FAULT_REPORTERS 1
 
 class PageFaultsSoftReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                           nsISupports* aData)
+                           nsISupports* aData, bool aAnonymize)
   {
     struct rusage usage;
     int err = getrusage(RUSAGE_SELF, &usage);
     if (err != 0) {
       return NS_ERROR_FAILURE;
     }
     int64_t amount = usage.ru_minflt;
 
@@ -656,17 +656,17 @@ PageFaultsHardDistinguishedAmount(int64_
 }
 
 class PageFaultsHardReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                           nsISupports* aData)
+                           nsISupports* aData, bool aAnonymize)
   {
     int64_t amount = 0;
     nsresult rv = PageFaultsHardDistinguishedAmount(&amount);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return MOZ_COLLECT_REPORT(
       "page-faults-hard", KIND_OTHER, UNITS_COUNT_CUMULATIVE, amount,
 "The number of hard page faults (also known as 'major page faults') that have "
@@ -703,17 +703,17 @@ HeapOverheadRatio(jemalloc_stats_t* aSta
 }
 
 class JemallocHeapReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                           nsISupports* aData)
+                           nsISupports* aData, bool aAnonymize)
   {
     jemalloc_stats_t stats;
     jemalloc_stats(&stats);
 
     nsresult rv;
 
     rv = MOZ_COLLECT_REPORT(
       "heap-allocated", KIND_OTHER, UNITS_BYTES, stats.allocated,
@@ -807,17 +807,17 @@ NS_IMPL_ISUPPORTS(JemallocHeapReporter, 
 class AtomTablesReporter MOZ_FINAL : public nsIMemoryReporter
 {
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
 public:
   NS_DECL_ISUPPORTS
 
   NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                           nsISupports* aData)
+                           nsISupports* aData, bool aAnonymize)
   {
     return MOZ_COLLECT_REPORT(
       "explicit/atom-tables", KIND_HEAP, UNITS_BYTES,
       NS_SizeOfAtomTablesIncludingThis(MallocSizeOf),
       "Memory used by the dynamic and static atoms tables.");
   }
 };
 NS_IMPL_ISUPPORTS(AtomTablesReporter, nsIMemoryReporter)
@@ -828,17 +828,17 @@ namespace mozilla {
 namespace dmd {
 
 class DMDReporter MOZ_FINAL : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
-                            nsISupports* aData)
+                            nsISupports* aData, bool aAnonymize)
   {
     dmd::Sizes sizes;
     dmd::SizeOf(&sizes);
 
 #define REPORT(_path, _amount, _desc)                                         \
   do {                                                                        \
     nsresult rv;                                                              \
     rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path),   \
@@ -986,30 +986,33 @@ nsMemoryReporterManager::DecrementNumChi
                        mNumChildProcesses);
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::GetReports(
   nsIHandleReportCallback* aHandleReport,
   nsISupports* aHandleReportData,
   nsIFinishReportingCallback* aFinishReporting,
-  nsISupports* aFinishReportingData)
+  nsISupports* aFinishReportingData,
+  bool aAnonymize)
 {
   return GetReportsExtended(aHandleReport, aHandleReportData,
                             aFinishReporting, aFinishReportingData,
+                            aAnonymize,
                             /* minimize = */ false,
                             /* DMDident = */ nsString());
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::GetReportsExtended(
   nsIHandleReportCallback* aHandleReport,
   nsISupports* aHandleReportData,
   nsIFinishReportingCallback* aFinishReporting,
   nsISupports* aFinishReportingData,
+  bool aAnonymize,
   bool aMinimize,
   const nsAString& aDMDDumpIdent)
 {
   nsresult rv;
 
   // Memory reporters are not necessarily threadsafe, so this function must
   // be called from the main thread.
   if (!NS_IsMainThread()) {
@@ -1032,41 +1035,43 @@ nsMemoryReporterManager::GetReportsExten
   if (mNumChildProcesses > 0) {
     // Request memory reports from child processes.  We do this *before*
     // collecting reports for this process so each process can collect
     // reports in parallel.
     nsCOMPtr<nsIObserverService> obs =
       do_GetService("@mozilla.org/observer-service;1");
     NS_ENSURE_STATE(obs);
 
-    nsPrintfCString genStr("generation=%x minimize=%d DMDident=",
-                           generation, aMinimize ? 1 : 0);
+    nsPrintfCString genStr("generation=%x anonymize=%d minimize=%d DMDident=",
+                           generation, aAnonymize ? 1 : 0, aMinimize ? 1 : 0);
     nsAutoString msg = NS_ConvertUTF8toUTF16(genStr);
     msg += aDMDDumpIdent;
 
     obs->NotifyObservers(nullptr, "child-memory-reporter-request",
                          msg.get());
 
     nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
     NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
     rv = timer->InitWithFuncCallback(TimeoutCallback,
                                      this, kTimeoutLengthMS,
                                      nsITimer::TYPE_ONE_SHOT);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mGetReportsState = new GetReportsState(generation,
+                                           aAnonymize,
                                            timer,
                                            mNumChildProcesses,
                                            aHandleReport,
                                            aHandleReportData,
                                            aFinishReporting,
                                            aFinishReportingData,
                                            aDMDDumpIdent);
   } else {
     mGetReportsState = new GetReportsState(generation,
+                                           aAnonymize,
                                            nullptr,
                                            /* mNumChildProcesses = */ 0,
                                            aHandleReport,
                                            aHandleReportData,
                                            aFinishReporting,
                                            aFinishReportingData,
                                            aDMDDumpIdent);
   }
@@ -1081,17 +1086,17 @@ nsMemoryReporterManager::GetReportsExten
 
 nsresult
 nsMemoryReporterManager::StartGettingReports()
 {
   GetReportsState* s = mGetReportsState;
 
   // Get reports for this process.
   GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
-                                   s->mDMDDumpIdent);
+                                   s->mAnonymize, s->mDMDDumpIdent);
   s->mParentDone = true;
 
   // If there are no remaining child processes, we can finish up immediately.
   return (s->mNumChildProcessesCompleted >= s->mNumChildProcesses)
     ? FinishReporting()
     : NS_OK;
 }
 
@@ -1111,28 +1116,26 @@ WeakEnumerator(nsPtrHashKey<nsIMemoryRep
   MemoryReporterArray* allReporters = static_cast<MemoryReporterArray*>(aData);
   allReporters->AppendElement(aElem->GetKey());
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::GetReportsForThisProcess(
   nsIHandleReportCallback* aHandleReport,
-  nsISupports* aHandleReportData)
+  nsISupports* aHandleReportData, bool aAnonymize)
 {
-  return GetReportsForThisProcessExtended(aHandleReport,
-                                          aHandleReportData,
-                                          nsString());
+  return GetReportsForThisProcessExtended(aHandleReport, aHandleReportData,
+                                          aAnonymize, nsString());
 }
 
 NS_IMETHODIMP
 nsMemoryReporterManager::GetReportsForThisProcessExtended(
-  nsIHandleReportCallback* aHandleReport,
-  nsISupports* aHandleReportData,
-  const nsAString& aDMDDumpIdent)
+  nsIHandleReportCallback* aHandleReport, nsISupports* aHandleReportData,
+  bool aAnonymize, const nsAString& aDMDDumpIdent)
 {
   // Memory reporters are not necessarily threadsafe, so this function must
   // be called from the main thread.
   if (!NS_IsMainThread()) {
     MOZ_CRASH();
   }
 
 #ifdef MOZ_DMD
@@ -1145,17 +1148,18 @@ nsMemoryReporterManager::GetReportsForTh
 
   MemoryReporterArray allReporters;
   {
     mozilla::MutexAutoLock autoLock(mMutex);
     mStrongReporters->EnumerateEntries(StrongEnumerator, &allReporters);
     mWeakReporters->EnumerateEntries(WeakEnumerator, &allReporters);
   }
   for (uint32_t i = 0; i < allReporters.Length(); i++) {
-    allReporters[i]->CollectReports(aHandleReport, aHandleReportData);
+    allReporters[i]->CollectReports(aHandleReport, aHandleReportData,
+                                    aAnonymize);
   }
 
 #ifdef MOZ_DMD
   if (!aDMDDumpIdent.IsEmpty()) {
     return nsMemoryInfoDumper::DumpDMD(aDMDDumpIdent);
   }
 #endif
 
@@ -1492,17 +1496,21 @@ nsMemoryReporterManager::GetExplicit(int
   // 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> handleReport = new ExplicitCallback();
   nsRefPtr<Int64Wrapper> wrappedExplicitSize = new Int64Wrapper();
 
-  GetReportsForThisProcess(handleReport, wrappedExplicitSize);
+  // Anonymization doesn't matter here, because we're only summing all the
+  // reported values. Enable it anyway because it's slightly faster, since it
+  // doesn't have to get URLs, find notable strings, etc.
+  GetReportsForThisProcess(handleReport, wrappedExplicitSize,
+                           /* anonymize = */ true);
 
   *aAmount = wrappedExplicitSize->mValue;
 
   return NS_OK;
 #endif // HAVE_JEMALLOC_STATS
 }
 
 NS_IMETHODIMP
@@ -1974,16 +1982,16 @@ NS_IMPL_ISUPPORTS(DoNothingCallback, nsI
 void
 RunReportersForThisProcess()
 {
   nsCOMPtr<nsIMemoryReporterManager> mgr =
     do_GetService("@mozilla.org/memory-reporter-manager;1");
 
   nsRefPtr<DoNothingCallback> doNothing = new DoNothingCallback();
 
-  mgr->GetReportsForThisProcess(doNothing, nullptr);
+  mgr->GetReportsForThisProcess(doNothing, nullptr, /* anonymize = */ false);
 }
 
 } // namespace dmd
 } // namespace mozilla
 
 #endif  // defined(MOZ_DMD)
 
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -188,34 +188,36 @@ private:
   WeakReportersTable* mSavedWeakReporters;
 
   uint32_t mNumChildProcesses;
   uint32_t mNextGeneration;
 
   struct GetReportsState
   {
     uint32_t                             mGeneration;
+    bool                                 mAnonymize;
     nsCOMPtr<nsITimer>                   mTimer;
     uint32_t                             mNumChildProcesses;
     uint32_t                             mNumChildProcessesCompleted;
     bool                                 mParentDone;
     nsCOMPtr<nsIHandleReportCallback>    mHandleReport;
     nsCOMPtr<nsISupports>                mHandleReportData;
     nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
     nsCOMPtr<nsISupports>                mFinishReportingData;
     nsString                             mDMDDumpIdent;
 
-    GetReportsState(uint32_t aGeneration, nsITimer* aTimer,
+    GetReportsState(uint32_t aGeneration, bool aAnonymize, nsITimer* aTimer,
                     uint32_t aNumChildProcesses,
                     nsIHandleReportCallback* aHandleReport,
                     nsISupports* aHandleReportData,
                     nsIFinishReportingCallback* aFinishReporting,
                     nsISupports* aFinishReportingData,
                     const nsAString& aDMDDumpIdent)
       : mGeneration(aGeneration)
+      , mAnonymize(aAnonymize)
       , mTimer(aTimer)
       , mNumChildProcesses(aNumChildProcesses)
       , mNumChildProcessesCompleted(0)
       , mParentDone(false)
       , mHandleReport(aHandleReport)
       , mHandleReportData(aHandleReportData)
       , mFinishReporting(aFinishReporting)
       , mFinishReportingData(aFinishReportingData)
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -368,17 +368,18 @@ public:
 
     static void Free(const void*, void* p)
     {
         return CountingFree(p);
     }
 
 private:
     NS_IMETHODIMP
-    CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+    CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+                   bool aAnonymize)
     {
         return MOZ_COLLECT_REPORT(
             "explicit/icu", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
             "Memory used by ICU, a Unicode and globalization support library.");
     }
 };
 
 NS_IMPL_ISUPPORTS(ICUReporter, nsIMemoryReporter)
@@ -388,17 +389,18 @@ NS_IMPL_ISUPPORTS(ICUReporter, nsIMemory
 class OggReporter MOZ_FINAL : public nsIMemoryReporter,
                               public CountingAllocatorBase<OggReporter>
 {
 public:
     NS_DECL_ISUPPORTS
 
 private:
     NS_IMETHODIMP
-    CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+    CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+                   bool aAnonymize)
     {
         return MOZ_COLLECT_REPORT(
             "explicit/media/libogg", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
             "Memory allocated through libogg for Ogg, Theora, and related media files.");
     }
 };
 
 NS_IMPL_ISUPPORTS(OggReporter, nsIMemoryReporter)
@@ -409,17 +411,18 @@ NS_IMPL_ISUPPORTS(OggReporter, nsIMemory
 class VPXReporter MOZ_FINAL : public nsIMemoryReporter,
                               public CountingAllocatorBase<VPXReporter>
 {
 public:
     NS_DECL_ISUPPORTS
 
 private:
     NS_IMETHODIMP
-    CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+    CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+                   bool aAnonymize)
     {
         return MOZ_COLLECT_REPORT(
             "explicit/media/libvpx", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
             "Memory allocated through libvpx for WebM media files.");
     }
 };
 
 NS_IMPL_ISUPPORTS(VPXReporter, nsIMemoryReporter)
@@ -431,17 +434,18 @@ NS_IMPL_ISUPPORTS(VPXReporter, nsIMemory
 class NesteggReporter MOZ_FINAL : public nsIMemoryReporter
                                 , public CountingAllocatorBase<NesteggReporter>
 {
 public:
     NS_DECL_ISUPPORTS
 
 private:
     NS_IMETHODIMP
-    CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData)
+    CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+                   bool aAnonymize)
     {
         return MOZ_COLLECT_REPORT(
             "explicit/media/libnestegg", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
             "Memory allocated through libnestegg for WebM media files.");
     }
 };
 
 NS_IMPL_ISUPPORTS(NesteggReporter, nsIMemoryReporter)
--- a/xpcom/components/nsCategoryManager.cpp
+++ b/xpcom/components/nsCategoryManager.cpp
@@ -474,17 +474,17 @@ nsCategoryManager::get_category(const ch
   }
   return node;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(CategoryManagerMallocSizeOf)
 
 NS_IMETHODIMP
 nsCategoryManager::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                  nsISupports* aData)
+                                  nsISupports* aData, bool aAnonymize)
 {
   return MOZ_COLLECT_REPORT(
     "explicit/xpcom/category-manager", KIND_HEAP, UNITS_BYTES,
     SizeOfIncludingThis(CategoryManagerMallocSizeOf),
     "Memory used for the XPCOM category manager.");
 }
 
 static size_t
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -1684,17 +1684,17 @@ SizeOfContractIDsEntryExcludingThis(nsCS
     // (which measures them in SizeOfFactoriesEntryExcludingThis).
     return aKey.SizeOfExcludingThisMustBeUnshared(aMallocSizeOf);
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(ComponentManagerMallocSizeOf)
 
 NS_IMETHODIMP
 nsComponentManagerImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                       nsISupports* aData)
+                                       nsISupports* aData, bool aAnonymize)
 {
     return MOZ_COLLECT_REPORT(
         "explicit/xpcom/component-manager", KIND_HEAP, UNITS_BYTES,
         SizeOfIncludingThis(ComponentManagerMallocSizeOf),
         "Memory used for the XPCOM component manager.");
 }
 
 size_t
--- a/xpcom/ds/nsObserverService.cpp
+++ b/xpcom/ds/nsObserverService.cpp
@@ -103,21 +103,22 @@ nsObserverService::CountReferents(nsObse
         referentCount->suspectObservers.AppendElement(suspect);
     }
 
     return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 nsObserverService::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                  nsISupports* aData)
+                                  nsISupports* aData, bool aAnonymize)
 {
     ObserverServiceReferentCount referentCount;
     mObserverTopicTable.EnumerateEntries(CountReferents, &referentCount);
 
+    // These aren't privacy-sensitive and so don't need anonymizing.
     nsresult rv;
     for (uint32_t i = 0; i < referentCount.suspectObservers.Length(); i++) {
         SuspectObserver& suspect = referentCount.suspectObservers[i];
         nsPrintfCString suspectPath("observer-service-suspect/"
                                     "referent(topic=%s)",
                                     suspect.topic);
         rv = aHandleReport->Callback(/* process */ EmptyCString(),
             suspectPath, KIND_OTHER, UNITS_COUNT, suspect.referentCount,
--- a/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
+++ b/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
@@ -40,17 +40,17 @@ XPTInterfaceInfoManager::SizeOfIncluding
     n += mWorkingSet.mNameTable.SizeOfExcludingThis(nullptr, aMallocSizeOf);
     return n;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(XPTIMallocSizeOf)
 
 NS_IMETHODIMP
 XPTInterfaceInfoManager::CollectReports(nsIHandleReportCallback* aHandleReport,
-                                        nsISupports* aData)
+                                        nsISupports* aData, bool aAnonymize)
 {
     size_t amount = SizeOfIncludingThis(XPTIMallocSizeOf);
 
     // Measure gXPTIStructArena here, too.  This is a bit grotty because it
     // doesn't belong to the XPTIInterfaceInfoManager, but there's no
     // obviously better place to measure it.
     amount += XPT_SizeOfArena(gXPTIStructArena, XPTIMallocSizeOf);