Bug 967817 - Finish memory reporters for Web Audio. r=roc, njn
authorEric Rahm <erahm@mozilla.com>
Sun, 13 Apr 2014 11:08:10 -0700
changeset 197710 7c18257d0163e793150777c8e2812cb660501adf
parent 197709 8d25da069190f1fbefac5e1fb26121c40d565938
child 197711 ba276673a564f5906115959a1823250123f8ca4a
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, njn
bugs967817
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 967817 - Finish memory reporters for Web Audio. r=roc, njn
content/media/AudioNodeEngine.h
content/media/AudioNodeStream.cpp
content/media/AudioNodeStream.h
content/media/AudioSegment.h
content/media/AudioStream.cpp
content/media/AudioStream.h
content/media/MediaDecoder.cpp
content/media/MediaSegment.h
content/media/MediaStreamGraph.cpp
content/media/MediaStreamGraph.h
content/media/MediaStreamGraphImpl.h
content/media/SharedBuffer.h
content/media/StreamBuffer.h
content/media/TimeVarying.h
content/media/VideoSegment.h
content/media/webaudio/AnalyserNode.cpp
content/media/webaudio/AnalyserNode.h
content/media/webaudio/AudioBufferSourceNode.cpp
content/media/webaudio/AudioBufferSourceNode.h
content/media/webaudio/AudioDestinationNode.cpp
content/media/webaudio/AudioDestinationNode.h
content/media/webaudio/AudioNode.cpp
content/media/webaudio/AudioNode.h
content/media/webaudio/AudioParam.h
content/media/webaudio/AudioParamTimeline.h
content/media/webaudio/BiquadFilterNode.cpp
content/media/webaudio/BiquadFilterNode.h
content/media/webaudio/ChannelMergerNode.cpp
content/media/webaudio/ChannelMergerNode.h
content/media/webaudio/ChannelSplitterNode.cpp
content/media/webaudio/ChannelSplitterNode.h
content/media/webaudio/ConvolverNode.cpp
content/media/webaudio/ConvolverNode.h
content/media/webaudio/DelayBuffer.cpp
content/media/webaudio/DelayBuffer.h
content/media/webaudio/DelayNode.cpp
content/media/webaudio/DelayNode.h
content/media/webaudio/DynamicsCompressorNode.cpp
content/media/webaudio/DynamicsCompressorNode.h
content/media/webaudio/FFTBlock.h
content/media/webaudio/GainNode.cpp
content/media/webaudio/GainNode.h
content/media/webaudio/MediaElementAudioSourceNode.h
content/media/webaudio/MediaStreamAudioDestinationNode.cpp
content/media/webaudio/MediaStreamAudioDestinationNode.h
content/media/webaudio/MediaStreamAudioSourceNode.cpp
content/media/webaudio/MediaStreamAudioSourceNode.h
content/media/webaudio/OscillatorNode.cpp
content/media/webaudio/OscillatorNode.h
content/media/webaudio/PannerNode.cpp
content/media/webaudio/PannerNode.h
content/media/webaudio/PeriodicWave.cpp
content/media/webaudio/PeriodicWave.h
content/media/webaudio/ScriptProcessorNode.cpp
content/media/webaudio/ScriptProcessorNode.h
content/media/webaudio/WaveShaperNode.cpp
content/media/webaudio/WaveShaperNode.h
content/media/webaudio/blink/DirectConvolver.h
content/media/webaudio/blink/DynamicsCompressor.cpp
content/media/webaudio/blink/DynamicsCompressor.h
content/media/webaudio/blink/DynamicsCompressorKernel.cpp
content/media/webaudio/blink/DynamicsCompressorKernel.h
content/media/webaudio/blink/FFTConvolver.cpp
content/media/webaudio/blink/FFTConvolver.h
content/media/webaudio/blink/HRTFDatabase.cpp
content/media/webaudio/blink/HRTFDatabase.h
content/media/webaudio/blink/HRTFDatabaseLoader.cpp
content/media/webaudio/blink/HRTFDatabaseLoader.h
content/media/webaudio/blink/HRTFElevation.cpp
content/media/webaudio/blink/HRTFElevation.h
content/media/webaudio/blink/HRTFKernel.h
content/media/webaudio/blink/HRTFPanner.cpp
content/media/webaudio/blink/HRTFPanner.h
content/media/webaudio/blink/PeriodicWave.cpp
content/media/webaudio/blink/PeriodicWave.h
content/media/webaudio/blink/Reverb.cpp
content/media/webaudio/blink/Reverb.h
content/media/webaudio/blink/ReverbAccumulationBuffer.h
content/media/webaudio/blink/ReverbConvolver.cpp
content/media/webaudio/blink/ReverbConvolver.h
content/media/webaudio/blink/ReverbConvolverStage.cpp
content/media/webaudio/blink/ReverbConvolverStage.h
content/media/webaudio/blink/ReverbInputBuffer.h
--- a/content/media/AudioNodeEngine.h
+++ b/content/media/AudioNodeEngine.h
@@ -39,16 +39,22 @@ public:
 
   struct Storage {
     Storage()
     {
       mDataToFree = nullptr;
       mSampleData = nullptr;
     }
     ~Storage() { free(mDataToFree); }
+    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+    {
+      // NB: mSampleData might not be owned, if it is it just points to
+      //     mDataToFree.
+      return aMallocSizeOf(mDataToFree);
+    }
     void* mDataToFree;
     const float* mSampleData;
   };
 
   /**
    * This can be called on any thread.
    */
   uint32_t GetChannels() const { return mContents.Length(); }
@@ -69,19 +75,25 @@ public:
     s->mSampleData = aData;
   }
 
   /**
    * Put this object into an error state where there are no channels.
    */
   void Clear() { mContents.Clear(); }
 
-  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+  virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   {
-    return mContents.SizeOfExcludingThis(aMallocSizeOf);
+    size_t amount = ThreadSharedObject::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mContents.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < mContents.Length(); i++) {
+      amount += mContents[i].SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+    return amount;
   }
 
 private:
   AutoFallibleTArray<Storage,2> mContents;
 };
 
 /**
  * Allocates an AudioChunk with fresh buffers of WEBAUDIO_BLOCK_SIZE float samples.
@@ -323,16 +335,35 @@ public:
     MOZ_ASSERT(mNode != nullptr);
     mNodeMutex.AssertCurrentThreadOwns();
     mNode = nullptr;
   }
 
   uint16_t InputCount() const { return mInputCount; }
   uint16_t OutputCount() const { return mOutputCount; }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    // NB: |mNode| is tracked separately so it is excluded here.
+    return 0;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  void SizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+                           AudioNodeSizes& aUsage) const
+  {
+    aUsage.mEngine = SizeOfIncludingThis(aMallocSizeOf);
+    aUsage.mDomNode = mNode->SizeOfIncludingThis(aMallocSizeOf);
+    aUsage.mNodeType = mNode->NodeType();
+  }
+
 private:
   dom::AudioNode* mNode;
   Mutex mNodeMutex;
   const uint16_t mInputCount;
   const uint16_t mOutputCount;
 };
 
 }
--- a/content/media/AudioNodeStream.cpp
+++ b/content/media/AudioNodeStream.cpp
@@ -25,16 +25,54 @@ namespace mozilla {
  * Note: This must be a different value than MEDIA_STREAM_DEST_TRACK_ID
  */
 
 AudioNodeStream::~AudioNodeStream()
 {
   MOZ_COUNT_DTOR(AudioNodeStream);
 }
 
+size_t
+AudioNodeStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = 0;
+
+  // Not reported:
+  // - mEngine
+
+  amount += ProcessedMediaStream::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mLastChunks.SizeOfExcludingThis(aMallocSizeOf);
+  for (size_t i = 0; i < mLastChunks.Length(); i++) {
+    // NB: This is currently unshared only as there are instances of
+    //     double reporting in DMD otherwise.
+    amount += mLastChunks[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+  }
+
+  return amount;
+}
+
+size_t
+AudioNodeStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+void
+AudioNodeStream::SizeOfAudioNodesIncludingThis(MallocSizeOf aMallocSizeOf,
+                                               AudioNodeSizes& aUsage) const
+{
+  // Explicitly separate out the stream memory.
+  aUsage.mStream = SizeOfIncludingThis(aMallocSizeOf);
+
+  if (mEngine) {
+    // This will fill out the rest of |aUsage|.
+    mEngine->SizeOfIncludingThis(aMallocSizeOf, aUsage);
+  }
+}
+
 void
 AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
                                         double aStreamTime)
 {
   class Message : public ControlMessage {
   public:
     Message(AudioNodeStream* aStream, uint32_t aIndex, MediaStream* aRelativeToStream,
             double aStreamTime)
--- a/content/media/AudioNodeStream.h
+++ b/content/media/AudioNodeStream.h
@@ -150,16 +150,22 @@ public:
                                       double aSeconds);
   /**
    * Get the destination stream time in seconds corresponding to a position on
    * this stream.
    */
   double DestinationTimeFromTicks(AudioNodeStream* aDestination,
                                   TrackTicks aPosition);
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
+  void SizeOfAudioNodesIncludingThis(MallocSizeOf aMallocSizeOf,
+                                     AudioNodeSizes& aUsage) const;
+
 protected:
   void AdvanceOutputSegment();
   void FinishOutput();
   void AccumulateInputChunk(uint32_t aInputIndex, const AudioChunk& aChunk,
                             AudioChunk* aBlock,
                             nsTArray<float>* aDownmixBuffer);
   void UpMixDownMixChunk(const AudioChunk* aChunk, uint32_t aOutputChannelCount,
                          nsTArray<const void*>& aOutputChannels,
--- a/content/media/AudioSegment.h
+++ b/content/media/AudioSegment.h
@@ -14,21 +14,38 @@
 #include "mozilla/TimeStamp.h"
 #endif
 
 namespace mozilla {
 
 template<typename T>
 class SharedChannelArrayBuffer : public ThreadSharedObject {
 public:
-  SharedChannelArrayBuffer(nsTArray<nsTArray<T>>* aBuffers)
+  SharedChannelArrayBuffer(nsTArray<nsTArray<T> >* aBuffers)
   {
     mBuffers.SwapElements(*aBuffers);
   }
-  nsTArray<nsTArray<T>> mBuffers;
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = 0;
+    amount += mBuffers.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < mBuffers.Length(); i++) {
+      amount += mBuffers[i].SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  nsTArray<nsTArray<T> > mBuffers;
 };
 
 class AudioStream;
 class AudioMixer;
 
 /**
  * For auto-arrays etc, guess this as the common number of channels.
  */
@@ -109,16 +126,37 @@ struct AudioChunk {
     mBuffer = nullptr;
     mChannelData.Clear();
     mDuration = aDuration;
     mVolume = 1.0f;
     mBufferFormat = AUDIO_FORMAT_SILENCE;
   }
   int ChannelCount() const { return mChannelData.Length(); }
 
+  size_t SizeOfExcludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const
+  {
+    return SizeOfExcludingThis(aMallocSizeOf, true);
+  }
+
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf, bool aUnshared) const
+  {
+    size_t amount = 0;
+
+    // Possibly owned:
+    // - mBuffer - Can hold data that is also in the decoded audio queue. If it
+    //             is not shared, or unshared == false it gets counted.
+    if (mBuffer && (!aUnshared || !mBuffer->IsShared())) {
+      amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    // Memory in the array is owned by mBuffer.
+    amount += mChannelData.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
   TrackTicks mDuration; // in frames within the buffer
   nsRefPtr<ThreadSharedObject> mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes
   nsTArray<const void*> mChannelData; // one pointer per channel; empty if and only if mBuffer is null
   float mVolume; // volume multiplier to apply (1.0f if mBuffer is nonnull)
   SampleFormat mBufferFormat; // format of frames in mBuffer (only meaningful if mBuffer is nonnull)
 #ifdef MOZILLA_INTERNAL_API
   mozilla::TimeStamp mTimeStamp;           // time at which this has been fetched from the MediaEngine
 #endif
@@ -226,13 +264,18 @@ public:
 
   int ChannelCount() {
     NS_WARN_IF_FALSE(!mChunks.IsEmpty(),
         "Cannot query channel count on a AudioSegment with no chunks.");
     return mChunks.IsEmpty() ? 0 : mChunks[0].mChannelData.Length();
   }
 
   static Type StaticType() { return AUDIO; }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 }
 
 #endif /* MOZILLA_AUDIOSEGMENT_H_ */
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -167,16 +167,32 @@ AudioStream::~AudioStream()
 {
   LOG(("AudioStream: delete %p, state %d", this, mState));
   Shutdown();
   if (mDumpFile) {
     fclose(mDumpFile);
   }
 }
 
+size_t
+AudioStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = aMallocSizeOf(this);
+
+  // Possibly add in the future:
+  // - mTimeStretcher
+  // - mLatencyLog
+  // - mCubebStream
+
+  amount += mInserts.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+
+  return amount;
+}
+
 /*static*/ void AudioStream::InitLibrary()
 {
 #ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("AudioStream");
 #endif
   PrefChanged(PREF_VOLUME_SCALE, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   PrefChanged(PREF_CUBEB_LATENCY, nullptr);
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -164,16 +164,23 @@ public:
       return mCount;
     }
     mStart += (mCount - aSize);
     mCount = aSize;
     mStart %= mCapacity;
     return mCount;
   }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = 0;
+    amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
 private:
   nsAutoArrayPtr<uint8_t> mBuffer;
   uint32_t mCapacity;
   uint32_t mStart;
   uint32_t mCount;
 };
 
 class AudioInitTask;
@@ -275,16 +282,18 @@ public:
   // This should be called before attempting to use the time stretcher.
   nsresult EnsureTimeStretcherInitialized();
   // Set playback rate as a multiple of the intrinsic playback rate. This is to
   // be called only with aPlaybackRate > 0.0.
   nsresult SetPlaybackRate(double aPlaybackRate);
   // Switch between resampling (if false) and time stretching (if true, default).
   nsresult SetPreservesPitch(bool aPreservesPitch);
 
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
 private:
   friend class AudioInitTask;
 
   // So we can call it asynchronously from AudioInitTask
   nsresult OpenCubeb(cubeb_stream_params &aParams,
                      LatencyRequest aLatencyRequest);
 
   void CheckForStart();
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -1765,16 +1765,18 @@ MediaMemoryTracker::CollectReports(nsIHa
 
   REPORT("explicit/media/decoded/audio", audio,
          "Memory used by decoded audio chunks.");
 
   REPORT("explicit/media/resources", resources,
          "Memory used by media resources including streaming buffers, caches, "
          "etc.");
 
+#undef REPORT
+
   return NS_OK;
 }
 
 MediaDecoderOwner*
 MediaDecoder::GetOwner()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mOwner;
--- a/content/media/MediaSegment.h
+++ b/content/media/MediaSegment.h
@@ -113,16 +113,26 @@ public:
    * Replace contents with disabled data of the same duration
    */
   virtual void ReplaceWithDisabled() = 0;
   /**
    * Remove all contents, setting duration to 0.
    */
   virtual void Clear() = 0;
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return 0;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   MediaSegment(Type aType) : mDuration(0), mType(aType)
   {
     MOZ_COUNT_CTOR(MediaSegment);
   }
 
   TrackTicks mDuration; // total of mDurations of all chunks
   Type mType;
@@ -240,16 +250,30 @@ public:
   }
 
 #ifdef MOZILLA_INTERNAL_API
   void GetStartTime(TimeStamp &aTime) {
     aTime = mChunks[0].mTimeStamp;
   }
 #endif
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = mChunks.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < mChunks.Length(); i++) {
+      amount += mChunks[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+    }
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   MediaSegmentBase(Type aType) : MediaSegment(aType) {}
 
   /**
    * Appends the contents of aSource to this segment, clearing aSource.
    */
   void AppendFromInternal(MediaSegmentBase<C, Chunk>* aSource)
   {
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -8,18 +8,20 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/unused.h"
 
 #include "AudioSegment.h"
 #include "VideoSegment.h"
 #include "nsContentUtils.h"
 #include "nsIAppShell.h"
 #include "nsIObserver.h"
+#include "nsPrintfCString.h"
 #include "nsServiceManagerUtils.h"
 #include "nsWidgetsCID.h"
+#include "prerror.h"
 #include "prlog.h"
 #include "mozilla/Attributes.h"
 #include "TrackUnionStream.h"
 #include "ImageContainer.h"
 #include "AudioChannelService.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "AudioNodeExternalInputStream.h"
@@ -1233,16 +1235,35 @@ MediaStreamGraphImpl::RunThread()
   }
   NS_ASSERTION(!messageQueue.IsEmpty(),
                "Shouldn't have started a graph with empty message queue!");
 
   uint32_t ticksProcessed = 0;
   AutoProfilerUnregisterThread autoUnregister;
 
   for (;;) {
+    // Check if a memory report has been requested.
+    {
+      MonitorAutoLock lock(mMemoryReportMonitor);
+      if (mNeedsMemoryReport) {
+        mNeedsMemoryReport = false;
+
+        for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+          AudioNodeStream* stream = mStreams[i]->AsAudioNodeStream();
+          if (stream) {
+            AudioNodeSizes usage;
+            stream->SizeOfAudioNodesIncludingThis(MallocSizeOf, usage);
+            mAudioStreamSizes.AppendElement(usage);
+          }
+        }
+
+        lock.Notify();
+      }
+    }
+
     // Update mCurrentTime to the min of the playing audio times, or using the
     // wall-clock time change if no audio is playing.
     UpdateCurrentTime();
 
     // Calculate independent action times for each batch of messages (each
     // batch corresponding to an event loop task). This isolates the performance
     // of different scripts to some extent.
     for (uint32_t i = 0; i < messageQueue.Length(); ++i) {
@@ -1514,17 +1535,17 @@ public:
     NS_ASSERTION(mGraph->mDetectedNotRunning,
                  "We should know the graph thread control loop isn't running!");
 
     mGraph->ShutdownThreads();
 
     // mGraph's thread is not running so it's OK to do whatever here
     if (mGraph->IsEmpty()) {
       // mGraph is no longer needed, so delete it.
-      delete mGraph;
+      mGraph->Destroy();
     } else {
       // The graph is not empty.  We must be in a forced shutdown, or a
       // non-realtime graph that has finished processing.  Some later
       // AppendMessage will detect that the manager has been emptied, and
       // delete it.
       NS_ASSERTION(mGraph->mForceShutDown || !mGraph->mRealtime,
                    "Not in forced shutdown?");
       for (uint32_t i = 0; i < mGraph->mStreams.Length(); ++i) {
@@ -1744,17 +1765,17 @@ MediaStreamGraphImpl::AppendMessage(Cont
     // graph has finished processing.
     aMessage->RunDuringShutdown();
     delete aMessage;
     if (IsEmpty() &&
         mLifecycleState >= LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION) {
       if (gGraph == this) {
         gGraph = nullptr;
       }
-      delete this;
+      Destroy();
     }
     return;
   }
 
   mCurrentTaskMessageQueue.AppendElement(aMessage);
   EnsureRunInStableState();
 }
 
@@ -1777,16 +1798,55 @@ MediaStream::MediaStream(DOMMediaStream*
   MOZ_COUNT_CTOR(MediaStream);
   // aWrapper should not already be connected to a MediaStream! It needs
   // to be hooked up to this stream, and since this stream is only just
   // being created now, aWrapper must not be connected to anything.
   NS_ASSERTION(!aWrapper || !aWrapper->GetStream(),
                "Wrapper already has another media stream hooked up to it!");
 }
 
+size_t
+MediaStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = 0;
+
+  // Not owned:
+  // - mGraph - Not reported here
+  // - mConsumers - elements
+  // Future:
+  // - mWrapper
+  // - mVideoOutputs - elements
+  // - mLastPlayedVideoFrame
+  // - mListeners - elements
+  // - mAudioOutputStreams - elements
+
+  amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mAudioOutputs.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mVideoOutputs.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mExplicitBlockerCount.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mListeners.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mMainThreadListeners.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mDisabledTrackIDs.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mBlocked.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mGraphUpdateIndices.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mConsumers.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mAudioOutputStreams.SizeOfExcludingThis(aMallocSizeOf);
+  for (size_t i = 0; i < mAudioOutputStreams.Length(); i++) {
+    amount += mAudioOutputStreams[i].SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  return amount;
+}
+
+size_t
+MediaStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 void
 MediaStream::Init()
 {
   MediaStreamGraphImpl* graph = GraphImpl();
   mBlocked.SetAtAndAfter(graph->mCurrentTime, true);
   mExplicitBlockerCount.SetAtAndAfter(graph->mCurrentTime, true);
   mExplicitBlockerCount.SetAtAndAfter(graph->mStateComputedTime, false);
 }
@@ -2589,24 +2649,40 @@ MediaStreamGraphImpl::MediaStreamGraphIm
   , mPostedRunInStableStateEvent(false)
   , mDetectedNotRunning(false)
   , mPostedRunInStableState(false)
   , mRealtime(aRealtime)
   , mNonRealtimeProcessing(false)
   , mStreamOrderDirty(false)
   , mLatencyLog(AsyncLatencyLogger::Get())
   , mMixer(nullptr)
+  , mMemoryReportMonitor("MSGIMemory")
+  , mSelfRef(MOZ_THIS_IN_INITIALIZER_LIST())
+  , mAudioStreamSizes()
+  , mNeedsMemoryReport(false)
 {
 #ifdef PR_LOGGING
   if (!gMediaStreamGraphLog) {
     gMediaStreamGraphLog = PR_NewLogModule("MediaStreamGraph");
   }
 #endif
 
   mCurrentTimeStamp = mInitialTimeStamp = mLastMainThreadUpdate = TimeStamp::Now();
+
+  RegisterWeakMemoryReporter(this);
+}
+
+void
+MediaStreamGraphImpl::Destroy()
+{
+  // First unregister from memory reporting.
+  UnregisterWeakMemoryReporter(this);
+
+  // Clear the self reference which will destroy this instance.
+  mSelfRef = nullptr;
 }
 
 NS_IMPL_ISUPPORTS1(MediaStreamGraphShutdownObserver, nsIObserver)
 
 static bool gShutdownObserverRegistered = false;
 
 NS_IMETHODIMP
 MediaStreamGraphShutdownObserver::Observe(nsISupports *aSubject,
@@ -2630,30 +2706,32 @@ MediaStreamGraph::GetInstance()
 
   if (!gGraph) {
     if (!gShutdownObserverRegistered) {
       gShutdownObserverRegistered = true;
       nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver());
     }
 
     gGraph = new MediaStreamGraphImpl(true);
+
     STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph));
 
     AudioStream::InitPreferredSampleRate();
   }
 
   return gGraph;
 }
 
 MediaStreamGraph*
 MediaStreamGraph::CreateNonRealtimeInstance()
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
 
   MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false);
+
   return graph;
 }
 
 void
 MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
   MOZ_ASSERT(aGraph->IsNonRealtime(), "Should not destroy the global graph here");
@@ -2664,16 +2742,86 @@ MediaStreamGraph::DestroyNonRealtimeInst
 
   if (!graph->mNonRealtimeProcessing) {
     // Start the graph, but don't produce anything
     graph->StartNonRealtimeProcessing(1, 0);
   }
   graph->ForceShutDown();
 }
 
+NS_IMPL_ISUPPORTS1(MediaStreamGraphImpl, nsIMemoryReporter)
+
+struct ArrayClearer
+{
+  ArrayClearer(nsTArray<AudioNodeSizes>& aArray) : mArray(aArray) {}
+  ~ArrayClearer() { mArray.Clear(); }
+  nsTArray<AudioNodeSizes>& mArray;
+};
+
+NS_IMETHODIMP
+MediaStreamGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
+                                     nsISupports* aData)
+{
+  // Clears out the report array after we're done with it.
+  ArrayClearer reportCleanup(mAudioStreamSizes);
+
+  {
+    MonitorAutoLock memoryReportLock(mMemoryReportMonitor);
+    mNeedsMemoryReport = true;
+
+    {
+      // Wake up the MSG thread.
+      MonitorAutoLock monitorLock(mMonitor);
+      EnsureImmediateWakeUpLocked(monitorLock);
+    }
+
+    // Wait for the report to complete.
+    nsresult rv;
+    while ((rv = memoryReportLock.Wait()) != NS_OK) {
+      if (PR_GetError() != PR_PENDING_INTERRUPT_ERROR) {
+        return rv;
+      }
+    }
+  }
+
+#define REPORT(_path, _amount, _desc)                                       \
+  do {                                                                      \
+    nsresult rv;                                                            \
+    rv = aHandleReport->Callback(EmptyCString(), _path,                     \
+                                 KIND_HEAP, UNITS_BYTES, _amount,           \
+                                 NS_LITERAL_CSTRING(_desc), aData);         \
+    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);
+    REPORT(domNodePath, usage.mDomNode,
+           "Memory used by AudioNode DOM objects (Web Audio).");
+
+    nsPrintfCString enginePath("explicit/webaudio/audio-node/%s/engine-objects",
+                                nodeType);
+    REPORT(enginePath, usage.mEngine,
+           "Memory used by AudioNode engine objects (Web Audio).");
+
+    nsPrintfCString streamPath("explicit/webaudio/audio-node/%s/stream-objects",
+                                nodeType);
+    REPORT(streamPath, usage.mStream,
+           "Memory used by AudioNode stream objects (Web Audio).");
+
+  }
+
+#undef REPORT
+
+  return NS_OK;
+}
+
 SourceMediaStream*
 MediaStreamGraph::CreateSourceStream(DOMMediaStream* aWrapper)
 {
   SourceMediaStream* stream = new SourceMediaStream(aWrapper);
   NS_ADDREF(stream);
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
   stream->SetGraphImpl(graph);
   graph->AppendMessage(new CreateMessage(stream));
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -221,16 +221,27 @@ public:
  * The listener is allowed to synchronously remove itself from the stream, but
  * not add or remove any other listeners.
  */
 class MainThreadMediaStreamListener {
 public:
   virtual void NotifyMainThreadStateChanged() = 0;
 };
 
+/**
+ * Helper struct used to keep track of memory usage by AudioNodes.
+ */
+struct AudioNodeSizes
+{
+  size_t mDomNode;
+  size_t mStream;
+  size_t mEngine;
+  nsCString mNodeType;
+};
+
 class MediaStreamGraphImpl;
 class SourceMediaStream;
 class ProcessedMediaStream;
 class MediaInputPort;
 class AudioNodeEngine;
 class AudioNodeExternalInputStream;
 class AudioNodeStream;
 struct AudioChunk;
@@ -505,16 +516,19 @@ public:
   }
 
   // Return true if the main thread needs to observe updates from this stream.
   virtual bool MainThreadNeedsUpdates() const
   {
     return true;
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
 protected:
   virtual void AdvanceTimeVaryingValuesToCurrentTime(GraphTime aCurrentTime, GraphTime aBlockedTime)
   {
     mBufferStartTime += aBlockedTime;
     mGraphUpdateIndices.InsertTimeAtStart(aBlockedTime);
     mGraphUpdateIndices.AdvanceCurrentTime(aCurrentTime);
     mExplicitBlockerCount.AdvanceCurrentTime(aCurrentTime);
 
@@ -572,16 +586,23 @@ protected:
     GraphTime mAudioPlaybackStartTime;
     // Amount of time that we've wanted to play silence because of the stream
     // blocking.
     MediaTime mBlockedAudioTime;
     // Last tick written to the audio output.
     TrackTicks mLastTickWritten;
     RefPtr<AudioStream> mStream;
     TrackID mTrackID;
+
+    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+    {
+      size_t amount = 0;
+      amount += mStream->SizeOfIncludingThis(aMallocSizeOf);
+      return amount;
+    }
   };
   nsTArray<AudioOutputStream> mAudioOutputStreams;
 
   /**
    * When true, this means the stream will be finished once all
    * buffered data has been consumed.
    */
   bool mFinished;
@@ -915,16 +936,32 @@ public:
    */
   MediaStreamGraphImpl* GraphImpl();
   MediaStreamGraph* Graph();
   /**
    * Sets the graph that owns this stream.  Should only be called once.
    */
   void SetGraphImpl(MediaStreamGraphImpl* aGraph);
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = 0;
+
+    // Not owned:
+    // - mSource
+    // - mDest
+    // - mGraph
+    return amount;
+  }
+
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   friend class MediaStreamGraphImpl;
   friend class MediaStream;
   friend class ProcessedMediaStream;
   // Never modified after Init()
   MediaStream* mSource;
   ProcessedMediaStream* mDest;
   uint32_t mFlags;
@@ -1011,16 +1048,29 @@ public:
 
   /**
    * Forward SetTrackEnabled() to the input MediaStream(s) and translate the ID
    */
   virtual void ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) {};
 
   bool InCycle() const { return mInCycle; }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = MediaStream::SizeOfExcludingThis(aMallocSizeOf);
+    // Not owned:
+    // - mInputs elements
+    amount += mInputs.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 
 protected:
   // This state is all accessed only on the media graph thread.
 
   // The list of all inputs that are currently enabled or waiting to be enabled.
   nsTArray<MediaInputPort*> mInputs;
   bool mAutofinish;
   // True if and only if this stream is in a cycle.
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -5,16 +5,17 @@
 
 #ifndef MOZILLA_MEDIASTREAMGRAPHIMPL_H_
 #define MOZILLA_MEDIASTREAMGRAPHIMPL_H_
 
 #include "MediaStreamGraph.h"
 
 #include "mozilla/Monitor.h"
 #include "mozilla/TimeStamp.h"
+#include "nsIMemoryReporter.h"
 #include "nsIThread.h"
 #include "nsIRunnable.h"
 #include "Latency.h"
 #include "mozilla/WeakPtr.h"
 
 namespace mozilla {
 
 template <typename T>
@@ -101,27 +102,36 @@ protected:
 /**
  * The implementation of a media stream graph. This class is private to this
  * file. It's not in the anonymous namespace because MediaStream needs to
  * be able to friend it.
  *
  * Currently we have one global instance per process, and one per each
  * OfflineAudioContext object.
  */
-class MediaStreamGraphImpl : public MediaStreamGraph {
+class MediaStreamGraphImpl : public MediaStreamGraph,
+                             public nsIMemoryReporter {
 public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIMEMORYREPORTER
+
   /**
    * Set aRealtime to true in order to create a MediaStreamGraph which provides
    * support for real-time audio and video.  Set it to false in order to create
    * a non-realtime instance which just churns through its inputs and produces
    * output.  Those objects currently only support audio, and are used to
    * implement OfflineAudioContext.  They do not support MediaStream inputs.
    */
   explicit MediaStreamGraphImpl(bool aRealtime);
-  virtual ~MediaStreamGraphImpl();
+
+  /**
+   * Unregisters memory reporting and deletes this instance. This should be
+   * called instead of calling the destructor directly.
+   */
+  void Destroy();
 
   // Main thread only.
   /**
    * This runs every time we need to sync state from the media graph thread
    * to the main thread while the main thread is not in the middle
    * of a script. It runs during a "stable state" (per HTML5) or during
    * an event posted to the main thread.
    */
@@ -573,13 +583,39 @@ public:
   /**
    * Hold a ref to the Latency logger
    */
   nsRefPtr<AsyncLatencyLogger> mLatencyLog;
   /**
    * If this is not null, all the audio output for the MSG will be mixed down.
    */
   nsAutoPtr<AudioMixer> mMixer;
+
+private:
+  virtual ~MediaStreamGraphImpl();
+
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
+  /**
+   * Used to signal that a memory report has been requested.
+   */
+  Monitor mMemoryReportMonitor;
+  /**
+   * This class uses manual memory management, and all pointers to it are raw
+   * pointers. However, in order for it to implement nsIMemoryReporter, it needs
+   * to implement nsISupports and so be ref-counted. So it maintains a single
+   * nsRefPtr to itself, giving it a ref-count of 1 during its entire lifetime,
+   * and Destroy() nulls this self-reference in order to trigger self-deletion.
+   */
+  nsRefPtr<MediaStreamGraphImpl> mSelfRef;
+  /**
+   * Used to pass memory report information across threads.
+   */
+  nsTArray<AudioNodeSizes> mAudioStreamSizes;
+  /**
+   * Indicates that the MSG thread should gather data for a memory report.
+   */
+  bool mNeedsMemoryReport;
 };
 
 }
 
 #endif /* MEDIASTREAMGRAPHIMPL_H_ */
--- a/content/media/SharedBuffer.h
+++ b/content/media/SharedBuffer.h
@@ -16,16 +16,27 @@ namespace mozilla {
 /**
  * Base class for objects with a thread-safe refcount and a virtual
  * destructor.
  */
 class ThreadSharedObject {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadSharedObject)
 
+  bool IsShared() { return mRefCnt.get() > 1; }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return 0;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~ThreadSharedObject() {}
 };
 
 /**
  * Heap-allocated chunk of arbitrary data with threadsafe refcounting.
  * Typically you would allocate one of these, fill it in, and then treat it as
@@ -47,18 +58,19 @@ public:
     }
     void* m = moz_xmalloc(size.value());
     nsRefPtr<SharedBuffer> p = new (m) SharedBuffer();
     NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) - reinterpret_cast<char*>(p.get())) % 4 == 0,
                  "SharedBuffers should be at least 4-byte aligned");
     return p.forget();
   }
 
-  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
-    return aMallocSizeOf(this);
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
 private:
   SharedBuffer() {}
 };
 
 }
 
--- a/content/media/StreamBuffer.h
+++ b/content/media/StreamBuffer.h
@@ -158,16 +158,25 @@ public:
     {
       return mSegment.forget();
     }
     void ForgetUpTo(TrackTicks aTime)
     {
       mSegment->ForgetUpTo(aTime);
     }
 
+    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+    {
+      size_t amount = aMallocSizeOf(this);
+      if (mSegment) {
+        amount += mSegment->SizeOfIncludingThis(aMallocSizeOf);
+      }
+      return amount;
+    }
+
   protected:
     friend class StreamBuffer;
 
     // Start offset is in ticks at rate mRate
     TrackTicks mStart;
     // The segment data starts at the start of the owning StreamBuffer, i.e.,
     // there's mStart silence/no video at the beginning.
     nsAutoPtr<MediaSegment> mSegment;
@@ -193,16 +202,26 @@ public:
   {
     MOZ_COUNT_CTOR(StreamBuffer);
   }
   ~StreamBuffer()
   {
     MOZ_COUNT_DTOR(StreamBuffer);
   }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = 0;
+    amount += mTracks.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < mTracks.Length(); i++) {
+      amount += mTracks[i]->SizeOfIncludingThis(aMallocSizeOf);
+    }
+    return amount;
+  }
+
   /**
    * Takes ownership of aSegment. Don't do this while iterating, or while
    * holding a Track reference.
    * aSegment must have aStart worth of null data.
    */
   Track& AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart, MediaSegment* aSegment)
   {
     NS_ASSERTION(TimeToTicksRoundDown(aRate, mTracksKnownTime) <= aStart,
--- a/content/media/TimeVarying.h
+++ b/content/media/TimeVarying.h
@@ -210,16 +210,21 @@ public:
     NS_ASSERTION(&aOther != this, "Can't self-append");
     SetAtAndAfter(aTimeOffset, aOther.mCurrent);
     for (uint32_t i = 0; i < aOther.mChanges.Length(); ++i) {
       const Entry& e = aOther.mChanges[i];
       SetAtAndAfter(aTimeOffset + e.mTime, e.mValue);
     }
   }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return mChanges.SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   struct Entry {
     Entry(Time aTime, const T& aValue) : mTime(aTime), mValue(aValue) {}
 
     // The time at which the value changes to mValue
     Time mTime;
     T mValue;
   };
--- a/content/media/VideoSegment.h
+++ b/content/media/VideoSegment.h
@@ -71,16 +71,23 @@ struct VideoChunk {
   void SetNull(TrackTicks aDuration)
   {
     mDuration = aDuration;
     mFrame.SetNull();
     mTimeStamp = TimeStamp();
   }
   void SetForceBlack(bool aForceBlack) { mFrame.SetForceBlack(aForceBlack); }
 
+  size_t SizeOfExcludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const
+  {
+    // Future:
+    // - mFrame
+    return 0;
+  }
+
   TrackTicks mDuration;
   VideoFrame mFrame;
   mozilla::TimeStamp mTimeStamp;
 };
 
 class VideoSegment : public MediaSegmentBase<VideoSegment, VideoChunk> {
 public:
   typedef mozilla::layers::Image Image;
@@ -116,13 +123,18 @@ public:
          !i.IsEnded(); i.Next()) {
       VideoChunk& chunk = *i;
       chunk.SetForceBlack(true);
     }
   }
 
   // Segment-generic methods not in MediaSegmentBase
   static Type StaticType() { return VIDEO; }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 }
 
 #endif /* MOZILLA_VIDEOSEGMENT_H_ */
--- a/content/media/webaudio/AnalyserNode.cpp
+++ b/content/media/webaudio/AnalyserNode.cpp
@@ -67,16 +67,21 @@ public:
     MutexAutoLock lock(NodeMutex());
 
     if (Node() &&
         aInput.mChannelData.Length() > 0) {
       nsRefPtr<TransferBuffer> transfer = new TransferBuffer(aStream, aInput);
       NS_DispatchToMainThread(transfer);
     }
   }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 AnalyserNode::AnalyserNode(AudioContext* aContext)
   : AudioNode(aContext,
               1,
               ChannelCountMode::Explicit,
               ChannelInterpretation::Speakers)
   , mAnalysisBlock(2048)
@@ -85,16 +90,32 @@ AnalyserNode::AnalyserNode(AudioContext*
   , mSmoothingTimeConstant(.8)
   , mWriteIndex(0)
 {
   mStream = aContext->Graph()->CreateAudioNodeStream(new AnalyserNodeEngine(this),
                                                      MediaStreamGraph::INTERNAL_STREAM);
   AllocateBuffer();
 }
 
+size_t
+AnalyserNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mAnalysisBlock.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mOutputBuffer.SizeOfExcludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+AnalyserNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 AnalyserNode::WrapObject(JSContext* aCx)
 {
   return AnalyserNodeBinding::Wrap(aCx, this);
 }
 
 void
 AnalyserNode::SetFftSize(uint32_t aValue, ErrorResult& aRv)
--- a/content/media/webaudio/AnalyserNode.h
+++ b/content/media/webaudio/AnalyserNode.h
@@ -48,16 +48,24 @@ public:
   }
   void SetMaxDecibels(double aValue, ErrorResult& aRv);
   double SmoothingTimeConstant() const
   {
     return mSmoothingTimeConstant;
   }
   void SetSmoothingTimeConstant(double aValue, ErrorResult& aRv);
 
+  virtual const char* NodeType() const
+  {
+    return "AnalyserNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   friend class AnalyserNodeEngine;
   void AppendChunk(const AudioChunk& aChunk);
   bool AllocateBuffer();
   bool FFTAnalysis();
   void ApplyBlackmanWindow(float* aBuffer, uint32_t aSize);
 
 private:
--- a/content/media/webaudio/AudioBufferSourceNode.cpp
+++ b/content/media/webaudio/AudioBufferSourceNode.cpp
@@ -477,16 +477,41 @@ public:
     // We've finished if we've gone past mStop, or if we're past mDuration when
     // looping is disabled.
     if (streamPosition >= mStop ||
         (!mLoop && mBufferPosition >= mBufferEnd && !mRemainingResamplerTail)) {
       *aFinished = true;
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mBuffer - shared w/ AudioNode
+    // - mPlaybackRateTimeline - shared w/ AudioNode
+
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+
+    // NB: We need to modify speex if we want the full memory picture, internal
+    //     fields that need measuring noted below.
+    // - mResampler->mem
+    // - mResampler->sinc_table
+    // - mResampler->last_sample
+    // - mResampler->magic_samples
+    // - mResampler->samp_frac_num
+    amount += aMallocSizeOf(mResampler);
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   double mStart; // including the fractional position between ticks
   // Low pass filter effects from the resampler mean that samples before the
   // start time are influenced by resampling the buffer.  mBeginProcessing
   // includes the extent of this filter.  The special value of -TRACK_TICKS_MAX
   // indicates that the resampler has begun processing.
   TrackTicks mBeginProcessing;
   TrackTicks mStop;
   nsRefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
@@ -529,16 +554,34 @@ AudioBufferSourceNode::AudioBufferSource
 
 AudioBufferSourceNode::~AudioBufferSourceNode()
 {
   if (Context()) {
     Context()->UnregisterAudioBufferSourceNode(this);
   }
 }
 
+size_t
+AudioBufferSourceNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  if (mBuffer) {
+    amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  amount += mPlaybackRate->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+AudioBufferSourceNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 AudioBufferSourceNode::WrapObject(JSContext* aCx)
 {
   return AudioBufferSourceNodeBinding::Wrap(aCx, this);
 }
 
 void
 AudioBufferSourceNode::Start(double aWhen, double aOffset,
--- a/content/media/webaudio/AudioBufferSourceNode.h
+++ b/content/media/webaudio/AudioBufferSourceNode.h
@@ -103,16 +103,24 @@ public:
     SendLoopParametersToStream();
   }
   void SendDopplerShiftToStream(double aDopplerShift);
 
   IMPL_EVENT_HANDLER(ended)
 
   virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
 
+  virtual const char* NodeType() const
+  {
+    return "AudioBufferSourceNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   friend class AudioBufferSourceNodeEngine;
   // START is sent during Start().
   // STOP is sent during Stop().
   // BUFFERSTART and BUFFEREND are sent when SetBuffer() and Start() have
   // been called (along with sending the buffer).
   enum EngineParameters {
     SAMPLE_RATE,
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -142,16 +142,28 @@ public:
     }
 
     nsRefPtr<OfflineAudioCompletionEvent> event =
         new OfflineAudioCompletionEvent(context, nullptr, nullptr);
     event->InitEvent(renderedBuffer);
     context->DispatchTrustedEvent(event);
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mInputChannels.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   // The input to the destination node is recorded in the mInputChannels buffer.
   // When this buffer fills up with mLength frames, the buffered input is sent
   // to the main thread in order to dispatch OfflineAudioCompletionEvent.
   InputChannels mInputChannels;
   // An index representing the next offset in mInputChannels to be written to.
   uint32_t mWriteIndex;
   // How many frames the OfflineAudioContext intends to produce.
@@ -183,16 +195,21 @@ public:
       mVolume = aParam;
     }
   }
 
   enum Parameters {
     VOLUME,
   };
 
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   float mVolume;
 };
 
 static bool UseAudioChannelService()
 {
   return Preferences::GetBool("media.useAudioChannelService");
 }
@@ -251,16 +268,31 @@ AudioDestinationNode::AudioDestinationNo
                                      /* useCapture = */ true,
                                      /* wantsUntrusted = */ false);
     }
 
     CreateAudioChannelAgent();
   }
 }
 
+size_t
+AudioDestinationNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  // Might be useful in the future:
+  // - mAudioChannelAgent
+  return amount;
+}
+
+size_t
+AudioDestinationNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 void
 AudioDestinationNode::DestroyMediaStream()
 {
   if (mAudioChannelAgent && !Context()->IsOffline()) {
     mAudioChannelAgent->StopPlaying();
     mAudioChannelAgent = nullptr;
 
     nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
--- a/content/media/webaudio/AudioDestinationNode.h
+++ b/content/media/webaudio/AudioDestinationNode.h
@@ -69,16 +69,24 @@ public:
 
   // An amount that should be added to the MediaStream's current time to
   // get the AudioContext.currentTime.
   double ExtraCurrentTime();
 
   // When aIsOnlyNode is true, this is the only node for the AudioContext.
   void SetIsOnlyNodeForContext(bool aIsOnlyNode);
 
+  virtual const char* NodeType() const
+  {
+    return "AudioDestinationNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   bool CheckAudioChannelPermissions(AudioChannel aValue);
   void CreateAudioChannelAgent();
 
   void SetCanPlay(bool aCanPlay);
 
   void NotifyStableState();
   void ScheduleStableStateNotification();
--- a/content/media/webaudio/AudioNode.cpp
+++ b/content/media/webaudio/AudioNode.cpp
@@ -72,16 +72,47 @@ AudioNode::~AudioNode()
   MOZ_ASSERT(mInputNodes.IsEmpty());
   MOZ_ASSERT(mOutputNodes.IsEmpty());
   MOZ_ASSERT(mOutputParams.IsEmpty());
   if (mContext) {
     mContext->UpdateNodeCount(-1);
   }
 }
 
+size_t
+AudioNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  // Not owned:
+  // - mContext
+  // - mStream
+  size_t amount = 0;
+
+  amount += mInputNodes.SizeOfExcludingThis(aMallocSizeOf);
+  for (size_t i = 0; i < mInputNodes.Length(); i++) {
+    amount += mInputNodes[i].SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  // Just measure the array. The entire audio node graph is measured via the
+  // MediaStreamGraph's streams, so we don't want to double-count the elements.
+  amount += mOutputNodes.SizeOfExcludingThis(aMallocSizeOf);
+
+  amount += mOutputParams.SizeOfExcludingThis(aMallocSizeOf);
+  for (size_t i = 0; i < mOutputParams.Length(); i++) {
+    amount += mOutputParams[i]->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  return amount;
+}
+
+size_t
+AudioNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 template <class InputNode>
 static uint32_t
 FindIndexOfNode(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode)
 {
   for (uint32_t i = 0; i < aInputNodes.Length(); ++i) {
     if (aInputNodes[i].mInputNode == aNode) {
       return i;
     }
--- a/content/media/webaudio/AudioNode.h
+++ b/content/media/webaudio/AudioNode.h
@@ -10,16 +10,17 @@
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/AudioNodeBinding.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "AudioContext.h"
 #include "MediaStreamGraph.h"
 #include "WebAudioUtils.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 
 namespace dom {
 
 class AudioContext;
 class AudioBufferSourceNode;
 class AudioParam;
@@ -165,16 +166,26 @@ public:
   struct InputNode {
     ~InputNode()
     {
       if (mStreamPort) {
         mStreamPort->Destroy();
       }
     }
 
+    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+    {
+      size_t amount = 0;
+      if (mStreamPort) {
+        amount += mStreamPort->SizeOfIncludingThis(aMallocSizeOf);
+      }
+
+      return amount;
+    }
+
     // Weak reference.
     AudioNode* mInputNode;
     nsRefPtr<MediaInputPort> mStreamPort;
     // The index of the input port this node feeds into.
     // This is not used for connections to AudioParams.
     uint32_t mInputPort;
     // The index of the output port this node comes out of.
     uint32_t mOutputPort;
@@ -203,16 +214,21 @@ public:
   void MarkActive() { Context()->RegisterActiveNode(this); }
   // Active nodes call MarkInactive() when they have finished producing sound
   // for the foreseeable future.
   // Do not call MarkInactive from a node destructor.  If the destructor is
   // called, then the node is already inactive.
   // MarkInactive() may delete |this|.
   void MarkInactive() { Context()->UnregisterActiveNode(this); }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
+  virtual const char* NodeType() const = 0;
+
 private:
   friend class AudioBufferSourceNode;
   // This could possibly delete 'this'.
   void DisconnectFromGraph();
 
 protected:
   static void Callback(AudioNode* aNode) { /* not implemented */ }
 
--- a/content/media/webaudio/AudioParam.h
+++ b/content/media/webaudio/AudioParam.h
@@ -148,16 +148,37 @@ public:
     return mInputNodes.AppendElement();
   }
 
   void DisconnectFromGraphAndDestroyStream();
 
   // May create the stream if it doesn't exist
   MediaStream* Stream();
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioParamTimeline::SizeOfExcludingThis(aMallocSizeOf);
+    // Not owned:
+    // - mNode
+
+    // Just count the array, actual nodes are counted in mNode.
+    amount += mInputNodes.SizeOfExcludingThis(aMallocSizeOf);
+
+    if (mNodeStreamPort) {
+      amount += mNodeStreamPort->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   nsCycleCollectingAutoRefCnt mRefCnt;
   NS_DECL_OWNINGTHREAD
 
 private:
   nsRefPtr<AudioNode> mNode;
   // For every InputNode, there is a corresponding entry in mOutputParams of the
   // InputNode's mInputNode.
--- a/content/media/webaudio/AudioParamTimeline.h
+++ b/content/media/webaudio/AudioParamTimeline.h
@@ -47,16 +47,27 @@ public:
   // Get the value of the AudioParam at time aTime + aCounter.
   // aCounter here is an offset to aTime if we try to get the value in ticks,
   // otherwise it should always be zero.  aCounter is meant to be used when
   // getting the value of an a-rate AudioParam for each tick inside an
   // AudioNodeEngine implementation.
   template<class TimeType>
   float GetValueAtTime(TimeType aTime, size_t aCounter = 0);
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return mStream ? mStream->SizeOfIncludingThis(aMallocSizeOf) : 0;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+
 private:
   float AudioNodeInputValue(size_t aCounter) const;
 
 protected:
   // This is created lazily when needed.
   nsRefPtr<MediaStream> mStream;
 };
 
--- a/content/media/webaudio/BiquadFilterNode.cpp
+++ b/content/media/webaudio/BiquadFilterNode.cpp
@@ -206,16 +206,32 @@ public:
       SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune);
 
       mBiquads[i].process(input,
                           static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])),
                           aInput.GetDuration());
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mSource - probably not owned
+    // - mDestination - probably not owned
+    // - AudioParamTimelines - counted in the AudioNode
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mBiquads.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   AudioNodeStream* mSource;
   AudioNodeStream* mDestination;
   BiquadFilterType mType;
   AudioParamTimeline mFrequency;
   AudioParamTimeline mDetune;
   AudioParamTimeline mQ;
   AudioParamTimeline mGain;
@@ -237,16 +253,47 @@ BiquadFilterNode::BiquadFilterNode(Audio
   , mGain(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
                          SendGainToStream, 0.f))
 {
   BiquadFilterNodeEngine* engine = new BiquadFilterNodeEngine(this, aContext->Destination());
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
 }
 
+
+size_t
+BiquadFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+
+  if (mFrequency) {
+    amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  if (mDetune) {
+    amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  if (mQ) {
+    amount += mQ->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  if (mGain) {
+    amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  return amount;
+}
+
+size_t
+BiquadFilterNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 BiquadFilterNode::WrapObject(JSContext* aCx)
 {
   return BiquadFilterNodeBinding::Wrap(aCx, this);
 }
 
 void
 BiquadFilterNode::SetType(BiquadFilterType aType)
--- a/content/media/webaudio/BiquadFilterNode.h
+++ b/content/media/webaudio/BiquadFilterNode.h
@@ -51,16 +51,24 @@ public:
   {
     return mGain;
   }
 
   void GetFrequencyResponse(const Float32Array& aFrequencyHz,
                             const Float32Array& aMagResponse,
                             const Float32Array& aPhaseResponse);
 
+  virtual const char* NodeType() const
+  {
+    return "BiquadFilterNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   static void SendFrequencyToStream(AudioNode* aNode);
   static void SendDetuneToStream(AudioNode* aNode);
   static void SendQToStream(AudioNode* aNode);
   static void SendGainToStream(AudioNode* aNode);
 
 private:
   BiquadFilterType mType;
--- a/content/media/webaudio/ChannelMergerNode.cpp
+++ b/content/media/webaudio/ChannelMergerNode.cpp
@@ -48,16 +48,21 @@ public:
         AudioBlockCopyChannelWithScale(
             static_cast<const float*>(aInput[i].mChannelData[j]),
             aInput[i].mVolume,
             static_cast<float*>(const_cast<void*>(aOutput[0].mChannelData[channelIndex])));
         ++channelIndex;
       }
     }
   }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 ChannelMergerNode::ChannelMergerNode(AudioContext* aContext,
                                      uint16_t aInputCount)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
--- a/content/media/webaudio/ChannelMergerNode.h
+++ b/content/media/webaudio/ChannelMergerNode.h
@@ -21,16 +21,26 @@ public:
                     uint16_t aInputCount);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   virtual uint16_t NumberOfInputs() const MOZ_OVERRIDE { return mInputCount; }
 
+  virtual const char* NodeType() const
+  {
+    return "ChannelMergerNode";
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   const uint16_t mInputCount;
 };
 
 }
 }
 
 #endif
--- a/content/media/webaudio/ChannelSplitterNode.cpp
+++ b/content/media/webaudio/ChannelSplitterNode.cpp
@@ -40,16 +40,21 @@ public:
             aInput[0].mVolume,
             static_cast<float*>(const_cast<void*>(aOutput[i].mChannelData[0])));
       } else {
         // Pad with silent channels if needed
         aOutput[i].SetNull(WEBAUDIO_BLOCK_SIZE);
       }
     }
   }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 ChannelSplitterNode::ChannelSplitterNode(AudioContext* aContext,
                                          uint16_t aOutputCount)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
--- a/content/media/webaudio/ChannelSplitterNode.h
+++ b/content/media/webaudio/ChannelSplitterNode.h
@@ -21,16 +21,26 @@ public:
                       uint16_t aOutputCount);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   virtual uint16_t NumberOfOutputs() const MOZ_OVERRIDE { return mOutputCount; }
 
+  virtual const char* NodeType() const
+  {
+    return "ChannelSplitterNode";
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   const uint16_t mOutputCount;
 };
 
 }
 }
 
 #endif
--- a/content/media/webaudio/ConvolverNode.cpp
+++ b/content/media/webaudio/ConvolverNode.cpp
@@ -149,16 +149,35 @@ public:
       mLeftOverData = mBufferLength;
       MOZ_ASSERT(mLeftOverData > 0);
     }
     AllocateAudioBlock(2, aOutput);
 
     mReverb->process(&input, aOutput, WEBAUDIO_BLOCK_SIZE);
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    if (mBuffer && !mBuffer->IsShared()) {
+      amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    if (mReverb) {
+      amount += mReverb->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   nsRefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
   nsAutoPtr<WebCore::Reverb> mReverb;
   int32_t mBufferLength;
   int32_t mLeftOverData;
   float mSampleRate;
   bool mUseBackgroundThreads;
   bool mNormalize;
@@ -170,16 +189,34 @@ ConvolverNode::ConvolverNode(AudioContex
               ChannelCountMode::Clamped_max,
               ChannelInterpretation::Speakers)
   , mNormalize(true)
 {
   ConvolverNodeEngine* engine = new ConvolverNodeEngine(this, mNormalize);
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
 }
 
+size_t
+ConvolverNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  if (mBuffer) {
+    // NB: mBuffer might be shared with the associated engine, by convention
+    //     the AudioNode will report.
+    amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
+  }
+  return amount;
+}
+
+size_t
+ConvolverNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 ConvolverNode::WrapObject(JSContext* aCx)
 {
   return ConvolverNodeBinding::Wrap(aCx, this);
 }
 
 void
 ConvolverNode::SetBuffer(JSContext* aCx, AudioBuffer* aBuffer, ErrorResult& aRv)
--- a/content/media/webaudio/ConvolverNode.h
+++ b/content/media/webaudio/ConvolverNode.h
@@ -49,16 +49,24 @@ public:
   {
     if (aMode == ChannelCountMode::Max) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
     AudioNode::SetChannelCountModeValue(aMode, aRv);
   }
 
+  virtual const char* NodeType() const
+  {
+    return "ConvolverNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   nsRefPtr<AudioBuffer> mBuffer;
   bool mNormalize;
 };
 
 
 } //end namespace dom
 } //end namespace mozilla
--- a/content/media/webaudio/DelayBuffer.cpp
+++ b/content/media/webaudio/DelayBuffer.cpp
@@ -7,16 +7,29 @@
 #include "DelayBuffer.h"
 
 #include "mozilla/PodOperations.h"
 #include "AudioChannelFormat.h"
 #include "AudioNodeEngine.h"
 
 namespace mozilla {
 
+size_t
+DelayBuffer::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = 0;
+  amount += mChunks.SizeOfExcludingThis(aMallocSizeOf);
+  for (size_t i = 0; i < mChunks.Length(); i++) {
+    amount += mChunks[i].SizeOfExcludingThis(aMallocSizeOf, false);
+  }
+
+  amount += mUpmixChannels.SizeOfExcludingThis(aMallocSizeOf);
+  return amount;
+}
+
 void
 DelayBuffer::Write(const AudioChunk& aInputChunk)
 {
   // We must have a reference to the buffer if there are channels
   MOZ_ASSERT(aInputChunk.IsNull() == !aInputChunk.mChannelData.Length());
 #ifdef DEBUG
   MOZ_ASSERT(!mHaveWrittenBlock);
   mHaveWrittenBlock = true;
--- a/content/media/webaudio/DelayBuffer.h
+++ b/content/media/webaudio/DelayBuffer.h
@@ -70,16 +70,18 @@ public:
 
   void Reset() {
     mChunks.Clear();
     mCurrentDelay = -1.0;
   };
 
   int MaxDelayTicks() const { return mMaxDelayTicks; }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
+
 private:
   void ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
                     const AudioChunk* aOutputChunk,
                     uint32_t aFirstChannel, uint32_t aNumChannelsToRead,
                     ChannelInterpretation aChannelInterpretation);
   bool EnsureBuffer();
   int PositionForDelay(int aDelay);
   int ChunkForPosition(int aPosition);
--- a/content/media/webaudio/DelayNode.cpp
+++ b/content/media/webaudio/DelayNode.cpp
@@ -154,16 +154,32 @@ public:
   {
     if (mLeftOverData <= 0) {
       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
     } else {
       UpdateOutputBlock(aOutput);
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    // Not owned:
+    // - mSource - probably not owned
+    // - mDestination - probably not owned
+    // - mDelay - shares ref with AudioNode, don't count
+    amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   AudioNodeStream* mSource;
   AudioNodeStream* mDestination;
   AudioParamTimeline mDelay;
   DelayBuffer mBuffer;
   double mMaxDelay;
   TrackTicks mLastOutputPosition;
   // How much data we have in our buffer which needs to be flushed out when our inputs
   // finish.
@@ -180,16 +196,30 @@ DelayNode::DelayNode(AudioContext* aCont
 {
   DelayNodeEngine* engine =
     new DelayNodeEngine(this, aContext->Destination(),
                         aContext->SampleRate() * aMaxDelay);
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
 }
 
+size_t
+DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mDelay->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+DelayNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 DelayNode::WrapObject(JSContext* aCx)
 {
   return DelayNodeBinding::Wrap(aCx, this);
 }
 
 void
 DelayNode::SendDelayToStream(AudioNode* aNode)
--- a/content/media/webaudio/DelayNode.h
+++ b/content/media/webaudio/DelayNode.h
@@ -30,16 +30,24 @@ public:
     return mDelay;
   }
 
   virtual const DelayNode* AsDelayNode() const MOZ_OVERRIDE
   {
     return this;
   }
 
+  virtual const char* NodeType() const
+  {
+    return "DelayNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   static void SendDelayToStream(AudioNode* aNode);
   friend class DelayNodeEngine;
 
 private:
   nsRefPtr<AudioParam> mDelay;
 };
 
--- a/content/media/webaudio/DynamicsCompressorNode.cpp
+++ b/content/media/webaudio/DynamicsCompressorNode.cpp
@@ -125,16 +125,33 @@ public:
 
     AllocateAudioBlock(channelCount, aOutput);
     mCompressor->process(&aInput, aOutput, aInput.GetDuration());
 
     SendReductionParamToMainThread(aStream,
                                    mCompressor->parameterValue(DynamicsCompressor::ParamReduction));
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mSource (probably)
+    // - mDestination (probably)
+    // - Don't count the AudioParamTimelines, their inner refs are owned by the
+    // AudioNode.
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mCompressor->sizeOfIncludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   void SendReductionParamToMainThread(AudioNodeStream* aStream, float aReduction)
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     class Command : public nsRunnable
     {
     public:
@@ -200,16 +217,34 @@ DynamicsCompressorNode::DynamicsCompress
   , mRelease(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
                             SendReleaseToStream, 0.25f))
 {
   DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination());
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
 }
 
+size_t
+DynamicsCompressorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mThreshold->SizeOfIncludingThis(aMallocSizeOf);
+  amount += mKnee->SizeOfIncludingThis(aMallocSizeOf);
+  amount += mRatio->SizeOfIncludingThis(aMallocSizeOf);
+  amount += mAttack->SizeOfIncludingThis(aMallocSizeOf);
+  amount += mRelease->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+DynamicsCompressorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 DynamicsCompressorNode::WrapObject(JSContext* aCx)
 {
   return DynamicsCompressorNodeBinding::Wrap(aCx, this);
 }
 
 void
 DynamicsCompressorNode::SendThresholdToStream(AudioNode* aNode)
--- a/content/media/webaudio/DynamicsCompressorNode.h
+++ b/content/media/webaudio/DynamicsCompressorNode.h
@@ -51,16 +51,24 @@ public:
   }
 
   // Called GetRelease to prevent clashing with the nsISupports::Release name
   AudioParam* GetRelease() const
   {
     return mRelease;
   }
 
+  virtual const char* NodeType() const
+  {
+    return "DynamicsCompressorNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   static void SendThresholdToStream(AudioNode* aNode);
   static void SendKneeToStream(AudioNode* aNode);
   static void SendRatioToStream(AudioNode* aNode);
   static void SendAttackToStream(AudioNode* aNode);
   static void SendReleaseToStream(AudioNode* aNode);
 
 private:
--- a/content/media/webaudio/FFTBlock.h
+++ b/content/media/webaudio/FFTBlock.h
@@ -123,16 +123,30 @@ public:
   {
     return mOutputBuffer[aIndex].r;
   }
   float ImagData(uint32_t aIndex) const
   {
     return mOutputBuffer[aIndex].i;
   }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = 0;
+    amount += aMallocSizeOf(mFFT);
+    amount += aMallocSizeOf(mIFFT);
+    amount += mOutputBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   FFTBlock(const FFTBlock& other) MOZ_DELETE;
   void operator=(const FFTBlock& other) MOZ_DELETE;
 
   void EnsureFFT()
   {
     if (!mFFT) {
       mFFT = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
--- a/content/media/webaudio/GainNode.cpp
+++ b/content/media/webaudio/GainNode.cpp
@@ -96,16 +96,30 @@ public:
         const float* inputBuffer = static_cast<const float*> (aInput.mChannelData[channel]);
         float* buffer = static_cast<float*> (const_cast<void*>
                           (aOutput->mChannelData[channel]));
         AudioBlockCopyChannelWithScale(inputBuffer, computedGain, buffer);
       }
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mSource (probably)
+    // - mDestination (probably)
+    // - mGain - Internal ref owned by AudioNode
+    return AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   AudioNodeStream* mSource;
   AudioNodeStream* mDestination;
   AudioParamTimeline mGain;
 };
 
 GainNode::GainNode(AudioContext* aContext)
   : AudioNode(aContext,
               2,
@@ -114,16 +128,30 @@ GainNode::GainNode(AudioContext* aContex
   , mGain(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
                          SendGainToStream, 1.0f))
 {
   GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
 }
 
+size_t
+GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+GainNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 GainNode::WrapObject(JSContext* aCx)
 {
   return GainNodeBinding::Wrap(aCx, this);
 }
 
 void
 GainNode::SendGainToStream(AudioNode* aNode)
--- a/content/media/webaudio/GainNode.h
+++ b/content/media/webaudio/GainNode.h
@@ -25,16 +25,24 @@ public:
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   AudioParam* Gain() const
   {
     return mGain;
   }
 
+  virtual const char* NodeType() const
+  {
+    return "GainNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   static void SendGainToStream(AudioNode* aNode);
 
 private:
   nsRefPtr<AudioParam> mGain;
 };
 
 }
--- a/content/media/webaudio/MediaElementAudioSourceNode.h
+++ b/content/media/webaudio/MediaElementAudioSourceNode.h
@@ -14,14 +14,24 @@ namespace dom {
 
 class MediaElementAudioSourceNode : public MediaStreamAudioSourceNode
 {
 public:
   MediaElementAudioSourceNode(AudioContext* aContext,
                               DOMMediaStream* aStream);
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  virtual const char* NodeType() const
+  {
+    return "MediaElementAudioSourceNode";
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 }
 }
 
 #endif
--- a/content/media/webaudio/MediaStreamAudioDestinationNode.cpp
+++ b/content/media/webaudio/MediaStreamAudioDestinationNode.cpp
@@ -43,16 +43,21 @@ public:
   {
     *aOutput = aInput;
     StreamBuffer::Track* track = mOutputStream->EnsureTrack(MEDIA_STREAM_DEST_TRACK_ID,
                                                             aStream->SampleRate());
     AudioSegment* segment = track->Get<AudioSegment>();
     segment->AppendAndConsumeChunk(aOutput);
   }
 
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   ProcessedMediaStream* mOutputStream;
 };
 
 // This callback is used to ensure that only the audio data for this track is audible
 static bool FilterAudioNodeStreamTrack(StreamBuffer::Track* aTrack)
 {
   return aTrack->GetID() == MEDIA_STREAM_DEST_TRACK_ID;
@@ -76,16 +81,32 @@ MediaStreamAudioDestinationNode::MediaSt
   mPort = tus->AllocateInputPort(mStream, 0);
 
   nsIDocument* doc = aContext->GetParentObject()->GetExtantDoc();
   if (doc) {
     mDOMStream->CombineWithPrincipal(doc->NodePrincipal());
   }
 }
 
+size_t
+MediaStreamAudioDestinationNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  // Future:
+  // - mDOMStream
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mPort->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+MediaStreamAudioDestinationNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 void
 MediaStreamAudioDestinationNode::DestroyMediaStream()
 {
   AudioNode::DestroyMediaStream();
   if (mPort) {
     mPort->Destroy();
     mPort = nullptr;
   }
--- a/content/media/webaudio/MediaStreamAudioDestinationNode.h
+++ b/content/media/webaudio/MediaStreamAudioDestinationNode.h
@@ -29,16 +29,24 @@ public:
 
   virtual void DestroyMediaStream() MOZ_OVERRIDE;
 
   DOMMediaStream* DOMStream() const
   {
     return mDOMStream;
   }
 
+  virtual const char* NodeType() const
+  {
+    return "MediaStreamAudioDestinationNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   nsRefPtr<DOMMediaStream> mDOMStream;
   nsRefPtr<MediaInputPort> mPort;
 };
 
 }
 }
 
--- a/content/media/webaudio/MediaStreamAudioSourceNode.cpp
+++ b/content/media/webaudio/MediaStreamAudioSourceNode.cpp
@@ -44,16 +44,32 @@ MediaStreamAudioSourceNode::MediaStreamA
                                                MediaInputPort::FLAG_BLOCK_INPUT);
   mInputStream->AddConsumerToKeepAlive(this);
 }
 
 MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode()
 {
 }
 
+size_t
+MediaStreamAudioSourceNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  // Future:
+  // - mInputStream
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mInputPort->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+MediaStreamAudioSourceNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 void
 MediaStreamAudioSourceNode::DestroyMediaStream()
 {
   if (mInputPort) {
     mInputPort->Destroy();
     mInputPort = nullptr;
   }
   AudioNode::DestroyMediaStream();
--- a/content/media/webaudio/MediaStreamAudioSourceNode.h
+++ b/content/media/webaudio/MediaStreamAudioSourceNode.h
@@ -26,16 +26,24 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioSourceNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   virtual void DestroyMediaStream() MOZ_OVERRIDE;
 
   virtual uint16_t NumberOfInputs() const MOZ_OVERRIDE { return 0; }
 
+  virtual const char* NodeType() const
+  {
+    return "MediaStreamAudioSourceNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   nsRefPtr<MediaInputPort> mInputPort;
   nsRefPtr<DOMMediaStream> mInputStream;
 };
 
 }
 }
 
--- a/content/media/webaudio/OscillatorNode.cpp
+++ b/content/media/webaudio/OscillatorNode.cpp
@@ -458,16 +458,42 @@ public:
         ComputeCustom(output, ticks, start, end);
         break;
       default:
         ComputeSilence(aOutput);
     };
 
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+
+    // Not owned:
+    // - mSource
+    // - mDestination
+    // - mFrequency (internal ref owned by node)
+    // - mDetune (internal ref owned by node)
+
+    if (mCustom) {
+      amount += mCustom->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    if (mPeriodicWave) {
+      amount += mPeriodicWave->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   DCBlocker mDCBlocker;
   AudioNodeStream* mSource;
   AudioNodeStream* mDestination;
   TrackTicks mStart;
   TrackTicks mStop;
   AudioParamTimeline mFrequency;
   AudioParamTimeline mDetune;
   OscillatorType mType;
@@ -505,16 +531,34 @@ OscillatorNode::OscillatorNode(AudioCont
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
   mStream->AddMainThreadListener(this);
 }
 
 OscillatorNode::~OscillatorNode()
 {
 }
 
+size_t
+OscillatorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+
+  // For now only report if we know for sure that it's not shared.
+  amount += mPeriodicWave->SizeOfExcludingThisIfNotShared(aMallocSizeOf);
+  amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf);
+  amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+OscillatorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 OscillatorNode::WrapObject(JSContext* aCx)
 {
   return OscillatorNodeBinding::Wrap(aCx, this);
 }
 
 void
 OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
--- a/content/media/webaudio/OscillatorNode.h
+++ b/content/media/webaudio/OscillatorNode.h
@@ -112,16 +112,24 @@ public:
     mType = OscillatorType::Custom;
     SendTypeToStream();
   }
 
   IMPL_EVENT_HANDLER(ended)
 
   virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
 
+  virtual const char* NodeType() const
+  {
+    return "OscillatorNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   static void SendFrequencyToStream(AudioNode* aNode);
   static void SendDetuneToStream(AudioNode* aNode);
   void SendTypeToStream();
   void SendPeriodicWaveToStream();
 
 private:
   OscillatorType mType;
--- a/content/media/webaudio/PannerNode.cpp
+++ b/content/media/webaudio/PannerNode.cpp
@@ -183,16 +183,31 @@ public:
 
   void EqualPowerPanningFunction(const AudioChunk& aInput, AudioChunk* aOutput);
   void HRTFPanningFunction(const AudioChunk& aInput, AudioChunk* aOutput);
 
   float LinearGainFunction(float aDistance);
   float InverseGainFunction(float aDistance);
   float ExponentialGainFunction(float aDistance);
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    if (mHRTFPanner) {
+      amount += mHRTFPanner->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   nsAutoPtr<HRTFPanner> mHRTFPanner;
   typedef void (PannerNodeEngine::*PanningModelFunction)(const AudioChunk& aInput, AudioChunk* aOutput);
   PanningModelFunction mPanningModelFunction;
   typedef float (PannerNodeEngine::*DistanceModelFunction)(float aDistance);
   DistanceModelFunction mDistanceModelFunction;
   ThreeDPoint mPosition;
   ThreeDPoint mOrientation;
   ThreeDPoint mVelocity;
@@ -237,16 +252,30 @@ PannerNode::PannerNode(AudioContext* aCo
 
 PannerNode::~PannerNode()
 {
   if (Context()) {
     Context()->UnregisterPannerNode(this);
   }
 }
 
+size_t
+PannerNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mSources.SizeOfExcludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+PannerNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 PannerNode::WrapObject(JSContext* aCx)
 {
   return PannerNodeBinding::Wrap(aCx, this);
 }
 
 void PannerNode::DestroyMediaStream()
 {
--- a/content/media/webaudio/PannerNode.h
+++ b/content/media/webaudio/PannerNode.h
@@ -240,16 +240,24 @@ public:
     SendDoubleParameterToStream(CONE_OUTER_GAIN, mConeOuterGain);
   }
 
   float ComputeDopplerShift();
   void SendDopplerToSourcesIfNeeded();
   void FindConnectedSources();
   void FindConnectedSources(AudioNode* aNode, nsTArray<AudioBufferSourceNode*>& aSources, std::set<AudioNode*>& aSeenNodes);
 
+  virtual const char* NodeType() const
+  {
+    return "PannerNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   friend class AudioListener;
   friend class PannerNodeEngine;
   enum EngineParameters {
     LISTENER_POSITION,
     LISTENER_FRONT_VECTOR, // unit length
     LISTENER_RIGHT_VECTOR, // unit length, orthogonal to LISTENER_FRONT_VECTOR
     LISTENER_VELOCITY,
--- a/content/media/webaudio/PeriodicWave.cpp
+++ b/content/media/webaudio/PeriodicWave.cpp
@@ -39,16 +39,29 @@ PeriodicWave::PeriodicWave(AudioContext*
     return;
   }
   PodCopy(buffer, aRealData, aLength);
   mCoefficients->SetData(0, buffer, buffer);
   PodCopy(buffer+aLength, aImagData, aLength);
   mCoefficients->SetData(1, nullptr, buffer+aLength);
 }
 
+size_t
+PeriodicWave::SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const
+{
+  // Not owned:
+  // - mContext
+  size_t amount = 0;
+  if (!mCoefficients->IsShared()) {
+    amount += mCoefficients->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  return amount;
+}
+
 JSObject*
 PeriodicWave::WrapObject(JSContext* aCx)
 {
   return PeriodicWaveBinding::Wrap(aCx, this);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/media/webaudio/PeriodicWave.h
+++ b/content/media/webaudio/PeriodicWave.h
@@ -42,16 +42,18 @@ public:
     return mLength;
   }
 
   ThreadSharedFloatArrayBufferList* GetThreadSharedBuffer() const
   {
     return mCoefficients;
   }
 
+  size_t SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const;
+
 private:
   nsRefPtr<AudioContext> mContext;
   nsRefPtr<ThreadSharedFloatArrayBufferList> mCoefficients;
   uint32_t mLength;
 };
 
 }
 }
--- a/content/media/webaudio/ScriptProcessorNode.cpp
+++ b/content/media/webaudio/ScriptProcessorNode.cpp
@@ -33,17 +33,29 @@ class SharedBuffers
 private:
   class OutputQueue
   {
   public:
     explicit OutputQueue(const char* aName)
       : mMutex(aName)
     {}
 
-    Mutex& Lock() { return mMutex; }
+    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+    {
+      mMutex.AssertCurrentThreadOwns();
+
+      size_t amount = 0;
+      for (size_t i = 0; i < mBufferList.size(); i++) {
+        amount += mBufferList[i].SizeOfExcludingThis(aMallocSizeOf, false);
+      }
+
+      return amount;
+    }
+
+    Mutex& Lock() const { return const_cast<OutputQueue*>(this)->mMutex; }
 
     size_t ReadyToConsume() const
     {
       mMutex.AssertCurrentThreadOwns();
       MOZ_ASSERT(!NS_IsMainThread());
       return mBufferList.size();
     }
 
@@ -90,16 +102,28 @@ public:
     : mOutputQueue("SharedBuffers::outputQueue")
     , mDelaySoFar(TRACK_TICKS_MAX)
     , mSampleRate(aSampleRate)
     , mLatency(0.0)
     , mDroppingBuffers(false)
   {
   }
 
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = aMallocSizeOf(this);
+
+    {
+      MutexAutoLock lock(mOutputQueue.Lock());
+      amount += mOutputQueue.SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
   // main thread
   void FinishProducingOutputBuffer(ThreadSharedFloatArrayBufferList* aBuffer,
                                    uint32_t aBufferSize)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     TimeStamp now = TimeStamp::Now();
 
@@ -288,16 +312,36 @@ public:
     if (mInputWriteIndex >= mBufferSize) {
       SendBuffersToMainThread(aStream);
       mInputWriteIndex -= mBufferSize;
       mSeenNonSilenceInput = false;
       AllocateInputBlock();
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mSharedBuffers
+    // - mSource (probably)
+    // - mDestination (probably)
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mInputChannels.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < mInputChannels.Length(); i++) {
+      amount += mInputChannels[i].SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   void AllocateInputBlock()
   {
     for (unsigned i = 0; i < mInputChannels.Length(); ++i) {
       if (!mInputChannels[i]) {
         mInputChannels[i] = new float[mBufferSize];
       }
     }
@@ -445,16 +489,30 @@ ScriptProcessorNode::ScriptProcessorNode
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
 }
 
 ScriptProcessorNode::~ScriptProcessorNode()
 {
 }
 
+size_t
+ScriptProcessorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mSharedBuffers->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+ScriptProcessorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 ScriptProcessorNode::WrapObject(JSContext* aCx)
 {
   return ScriptProcessorNodeBinding::Wrap(aCx, this);
 }
 
 }
 }
--- a/content/media/webaudio/ScriptProcessorNode.h
+++ b/content/media/webaudio/ScriptProcessorNode.h
@@ -84,16 +84,24 @@ public:
 
   uint32_t NumberOfOutputChannels() const
   {
     return mNumberOfOutputChannels;
   }
 
   using DOMEventTargetHelper::DispatchTrustedEvent;
 
+  virtual const char* NodeType() const
+  {
+    return "ScriptProcessorNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   nsAutoPtr<SharedBuffers> mSharedBuffers;
   const uint32_t mBufferSize;
   const uint32_t mNumberOfOutputChannels;
 };
 
 }
 }
--- a/content/media/webaudio/WaveShaperNode.cpp
+++ b/content/media/webaudio/WaveShaperNode.cpp
@@ -125,16 +125,26 @@ public:
 
     WebAudioUtils::SpeexResamplerProcess(mDownSampler, aChannel,
                                          inputData, &inSamples,
                                          aOutputData, &outSamples);
 
     MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE*aBlocks && outSamples == WEBAUDIO_BLOCK_SIZE);
   }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = 0;
+    // Future: properly measure speex memory
+    amount += aMallocSizeOf(mUpSampler);
+    amount += aMallocSizeOf(mDownSampler);
+    amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
 private:
   void Destroy()
   {
     if (mUpSampler) {
       speex_resampler_destroy(mUpSampler);
       mUpSampler = nullptr;
     }
     if (mDownSampler) {
@@ -239,16 +249,29 @@ public:
         mResampler.DownSample(i, outputBuffer, 4);
         break;
       default:
         NS_NOTREACHED("We should never reach here");
       }
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mCurve.SizeOfExcludingThis(aMallocSizeOf);
+    amount += mResampler.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   nsTArray<float> mCurve;
   OverSampleType mType;
   Resampler mResampler;
 };
 
 WaveShaperNode::WaveShaperNode(AudioContext* aContext)
   : AudioNode(aContext,
--- a/content/media/webaudio/WaveShaperNode.h
+++ b/content/media/webaudio/WaveShaperNode.h
@@ -34,16 +34,33 @@ public:
   void SetCurve(const Nullable<Float32Array>& aData);
 
   OverSampleType Oversample() const
   {
     return mType;
   }
   void SetOversample(OverSampleType aType);
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Possibly track in the future:
+    // - mCurve
+    return AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  virtual const char* NodeType() const
+  {
+    return "WaveShaperNode";
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   void ClearCurve();
 
 private:
   JS::Heap<JSObject*> mCurve;
   OverSampleType mType;
 };
 
--- a/content/media/webaudio/blink/DirectConvolver.h
+++ b/content/media/webaudio/blink/DirectConvolver.h
@@ -25,27 +25,36 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef DirectConvolver_h
 #define DirectConvolver_h
 
 #include "nsTArray.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 class DirectConvolver {
 public:
     DirectConvolver(size_t inputBlockSize);
 
     void process(const nsTArray<float>* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess);
 
     void reset();
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+    {
+        size_t amount = aMallocSizeOf(this);
+        amount += m_buffer.SizeOfExcludingThis(aMallocSizeOf);
+        return amount;
+    }
+
+
 private:
     size_t m_inputBlockSize;
 
     nsTArray<float> m_buffer;
 };
 
 } // namespace WebCore
 
--- a/content/media/webaudio/blink/DynamicsCompressor.cpp
+++ b/content/media/webaudio/blink/DynamicsCompressor.cpp
@@ -47,16 +47,39 @@ DynamicsCompressor::DynamicsCompressor(f
     m_lastFilterStageRatio = -1;
     m_lastAnchor = -1;
     m_lastFilterStageGain = -1;
 
     setNumberOfChannels(numberOfChannels);
     initializeParameters();
 }
 
+size_t DynamicsCompressor::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+    amount += m_preFilterPacks.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_preFilterPacks.Length(); i++) {
+        if (m_preFilterPacks[i]) {
+            amount += m_preFilterPacks[i]->sizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    amount += m_postFilterPacks.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_postFilterPacks.Length(); i++) {
+        if (m_postFilterPacks[i]) {
+            amount += m_postFilterPacks[i]->sizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    amount += m_sourceChannels.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_destinationChannels.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_compressor.sizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+}
+
 void DynamicsCompressor::setParameterValue(unsigned parameterID, float value)
 {
     MOZ_ASSERT(parameterID < ParamLast);
     if (parameterID < ParamLast)
         m_parameters[parameterID] = value;
 }
 
 void DynamicsCompressor::initializeParameters()
--- a/content/media/webaudio/blink/DynamicsCompressor.h
+++ b/content/media/webaudio/blink/DynamicsCompressor.h
@@ -29,16 +29,17 @@
 #ifndef DynamicsCompressor_h
 #define DynamicsCompressor_h
 
 #include "DynamicsCompressorKernel.h"
 #include "ZeroPole.h"
 
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 struct AudioChunk;
 }
 
 namespace WebCore {
 
 using mozilla::AudioChunk;
@@ -81,32 +82,38 @@ public:
     float parameterValue(unsigned parameterID);
 
     float sampleRate() const { return m_sampleRate; }
     float nyquist() const { return m_sampleRate / 2; }
 
     double tailTime() const { return 0; }
     double latencyTime() const { return m_compressor.latencyFrames() / static_cast<double>(sampleRate()); }
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 protected:
     unsigned m_numberOfChannels;
 
     // m_parameters holds the tweakable compressor parameters.
     float m_parameters[ParamLast];
     void initializeParameters();
 
     float m_sampleRate;
 
     // Emphasis filter controls.
     float m_lastFilterStageRatio;
     float m_lastAnchor;
     float m_lastFilterStageGain;
 
     typedef struct {
         ZeroPole filters[4];
+        size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+        {
+            return aMallocSizeOf(this);
+        }
     } ZeroPoleFilterPack4;
 
     // Per-channel emphasis filters.
     nsTArray<nsAutoPtr<ZeroPoleFilterPack4> > m_preFilterPacks;
     nsTArray<nsAutoPtr<ZeroPoleFilterPack4> > m_postFilterPacks;
 
     nsAutoArrayPtr<const float*> m_sourceChannels;
     nsAutoArrayPtr<float*> m_destinationChannels;
--- a/content/media/webaudio/blink/DynamicsCompressorKernel.cpp
+++ b/content/media/webaudio/blink/DynamicsCompressorKernel.cpp
@@ -68,16 +68,27 @@ DynamicsCompressorKernel::DynamicsCompre
 
     // Initializes most member variables
     reset();
 
     m_meteringReleaseK =
         static_cast<float>(WebAudioUtils::DiscreteTimeConstantForSampleRate(meteringReleaseTimeConstant, sampleRate));
 }
 
+size_t DynamicsCompressorKernel::sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = 0;
+    amount += m_preDelayBuffers.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_preDelayBuffers.Length(); i++) {
+        amount += m_preDelayBuffers[i].SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+}
+
 void DynamicsCompressorKernel::setNumberOfChannels(unsigned numberOfChannels)
 {
     if (m_preDelayBuffers.Length() == numberOfChannels)
         return;
 
     m_preDelayBuffers.Clear();
     for (unsigned i = 0; i < numberOfChannels; ++i)
         m_preDelayBuffers.AppendElement(new float[MaxPreDelayFrames]);
--- a/content/media/webaudio/blink/DynamicsCompressorKernel.h
+++ b/content/media/webaudio/blink/DynamicsCompressorKernel.h
@@ -26,16 +26,17 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef DynamicsCompressorKernel_h
 #define DynamicsCompressorKernel_h
 
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 class DynamicsCompressorKernel {
 public:
     DynamicsCompressorKernel(float sampleRate, unsigned numberOfChannels);
 
     void setNumberOfChannels(unsigned);
@@ -64,16 +65,18 @@ public:
     void reset();
 
     unsigned latencyFrames() const { return m_lastPreDelayFrames; }
 
     float sampleRate() const { return m_sampleRate; }
 
     float meteringGain() const { return m_meteringGain; }
 
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 protected:
     float m_sampleRate;
 
     float m_detectorAverage;
     float m_compressorGain;
 
     // Metering
     float m_meteringReleaseK;
--- a/content/media/webaudio/blink/FFTConvolver.cpp
+++ b/content/media/webaudio/blink/FFTConvolver.cpp
@@ -40,16 +40,31 @@ FFTConvolver::FFTConvolver(size_t fftSiz
   m_inputBuffer.SetLength(fftSize);
   PodZero(m_inputBuffer.Elements(), fftSize);
   m_outputBuffer.SetLength(fftSize);
   PodZero(m_outputBuffer.Elements(), fftSize);
   m_lastOverlapBuffer.SetLength(fftSize / 2);
   PodZero(m_lastOverlapBuffer.Elements(), fftSize / 2);
 }
 
+size_t FFTConvolver::sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = 0;
+    amount += m_frame.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_inputBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_outputBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_lastOverlapBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+}
+
+size_t FFTConvolver::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + sizeOfExcludingThis(aMallocSizeOf);
+}
+
 void FFTConvolver::process(FFTBlock* fftKernel, const float* sourceP, float* destP, size_t framesToProcess)
 {
     size_t halfSize = fftSize() / 2;
 
     // framesToProcess must be an exact multiple of halfSize,
     // or halfSize is a multiple of framesToProcess when halfSize > framesToProcess.
     bool isGood = !(halfSize % framesToProcess && framesToProcess % halfSize);
     MOZ_ASSERT(isGood);
--- a/content/media/webaudio/blink/FFTConvolver.h
+++ b/content/media/webaudio/blink/FFTConvolver.h
@@ -26,16 +26,17 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef FFTConvolver_h
 #define FFTConvolver_h
 
 #include "nsTArray.h"
 #include "mozilla/FFTBlock.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 typedef nsTArray<float> AudioFloatArray;
 using mozilla::FFTBlock;
 
 class FFTConvolver {
 public:
@@ -52,16 +53,19 @@ public:
     //
     // Processing in-place is allowed...
     void process(FFTBlock* fftKernel, const float* sourceP, float* destP, size_t framesToProcess);
 
     void reset();
 
     size_t fftSize() const { return m_frame.FFTSize(); }
 
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     FFTBlock m_frame;
 
     // Buffer input until we get fftSize / 2 samples then do an FFT
     size_t m_readWriteIndex;
     AudioFloatArray m_inputBuffer;
 
     // Stores output which we read a little at a time
--- a/content/media/webaudio/blink/HRTFDatabase.cpp
+++ b/content/media/webaudio/blink/HRTFDatabase.cpp
@@ -74,16 +74,27 @@ HRTFDatabase::HRTFDatabase(float sampleR
                 float x = static_cast<float>(jj) / static_cast<float>(InterpolationFactor);
                 m_elevations[i + jj] = HRTFElevation::createByInterpolatingSlices(m_elevations[i].get(), m_elevations[j].get(), x, sampleRate);
                 MOZ_ASSERT(m_elevations[i + jj].get());
             }
         }
     }
 }
 
+size_t HRTFDatabase::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+    amount += m_elevations.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_elevations.Length(); i++) {
+      amount += m_elevations[i]->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+}
+
 void HRTFDatabase::getKernelsFromAzimuthElevation(double azimuthBlend, unsigned azimuthIndex, double elevationAngle, HRTFKernel* &kernelL, HRTFKernel* &kernelR,
                                                   double& frameDelayL, double& frameDelayR)
 {
     unsigned elevationIndex = indexFromElevationAngle(elevationAngle);
     MOZ_ASSERT(elevationIndex < m_elevations.Length() && m_elevations.Length() > 0);
     
     if (!m_elevations.Length()) {
         kernelL = 0;
--- a/content/media/webaudio/blink/HRTFDatabase.h
+++ b/content/media/webaudio/blink/HRTFDatabase.h
@@ -27,16 +27,17 @@
  */
 
 #ifndef HRTFDatabase_h
 #define HRTFDatabase_h
 
 #include "HRTFElevation.h"
 #include "nsAutoRef.h"
 #include "nsTArray.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 class HRTFKernel;
 
 class HRTFDatabase {
 public:
     static nsReturnRef<HRTFDatabase> create(float sampleRate);
@@ -50,16 +51,18 @@ public:
     // Returns the number of different azimuth angles.
     static unsigned numberOfAzimuths() { return HRTFElevation::NumberOfTotalAzimuths; }
 
     float sampleRate() const { return m_sampleRate; }
 
     // Number of elevations loaded from resource.
     static const unsigned NumberOfRawElevations;
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     HRTFDatabase(const HRTFDatabase& other) MOZ_DELETE;
     void operator=(const HRTFDatabase& other) MOZ_DELETE;
 
     explicit HRTFDatabase(float sampleRate);
 
     // Minimum and maximum elevation angles (inclusive) for a HRTFDatabase.
     static const int MinElevation;
--- a/content/media/webaudio/blink/HRTFDatabaseLoader.cpp
+++ b/content/media/webaudio/blink/HRTFDatabaseLoader.cpp
@@ -83,16 +83,30 @@ HRTFDatabaseLoader::~HRTFDatabaseLoader(
         s_loaderMap->RemoveEntry(m_databaseSampleRate);
         if (s_loaderMap->Count() == 0) {
             delete s_loaderMap;
             s_loaderMap = nullptr;
         }
     }
 }
 
+size_t HRTFDatabaseLoader::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+
+    // NB: Need to make sure we're not competing with the loader thread.
+    const_cast<HRTFDatabaseLoader*>(this)->waitForLoaderThreadCompletion();
+
+    if (m_hrtfDatabase) {
+        amount += m_hrtfDatabase->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+}
+
 class HRTFDatabaseLoader::ProxyReleaseEvent MOZ_FINAL : public nsRunnable {
 public:
     explicit ProxyReleaseEvent(HRTFDatabaseLoader* loader) : mLoader(loader) {}
     NS_IMETHOD Run() MOZ_OVERRIDE
     {
         mLoader->MainThreadRelease();
         return NS_OK;
     }
--- a/content/media/webaudio/blink/HRTFDatabaseLoader.h
+++ b/content/media/webaudio/blink/HRTFDatabaseLoader.h
@@ -26,16 +26,17 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef HRTFDatabaseLoader_h
 #define HRTFDatabaseLoader_h
 
 #include "nsHashKeys.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "HRTFDatabase.h"
 
 template <class EntryType> class nsTHashtable;
 template <class T> class nsAutoRef;
 
 namespace WebCore {
 
@@ -88,16 +89,18 @@ public:
 
     float databaseSampleRate() const { return m_databaseSampleRate; }
 
     static void shutdown();
     
     // Called in asynchronous loading thread.
     void load();
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     // Both constructor and destructor must be called from the main thread.
     explicit HRTFDatabaseLoader(float sampleRate);
     ~HRTFDatabaseLoader();
     
     void ProxyRelease(); // any thread
     void MainThreadRelease(); // main thread only
     class ProxyReleaseEvent;
--- a/content/media/webaudio/blink/HRTFElevation.cpp
+++ b/content/media/webaudio/blink/HRTFElevation.cpp
@@ -45,16 +45,28 @@ const int numberOfElevations = MOZ_ARRAY
 
 const unsigned HRTFElevation::NumberOfTotalAzimuths = 360 / 15 * 8;
 
 const int rawSampleRate = irc_composite_c_r0195_sample_rate;
 
 // Number of frames in an individual impulse response.
 const size_t ResponseFrameSize = 256;
 
+size_t HRTFElevation::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+
+    amount += m_kernelListL.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_kernelListL.Length(); i++) {
+        amount += m_kernelListL[i]->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+}
+
 size_t HRTFElevation::fftSizeForSampleRate(float sampleRate)
 {
     // The IRCAM HRTF impulse responses were 512 sample-frames @44.1KHz,
     // but these have been truncated to 256 samples.
     // An FFT-size of twice impulse response size is used (for convolution).
     // So for sample rates of 44.1KHz an FFT size of 512 is good.
     // We double the FFT-size only for sample rates at least double this.
     // If the FFT size is too large then the impulse response will be padded
--- a/content/media/webaudio/blink/HRTFElevation.h
+++ b/content/media/webaudio/blink/HRTFElevation.h
@@ -26,16 +26,17 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef HRTFElevation_h
 #define HRTFElevation_h
 
 #include "HRTFKernel.h"
 #include "nsAutoRef.h"
+#include "mozilla/MemoryReporting.h"
 
 struct SpeexResamplerState_;
 typedef struct SpeexResamplerState_ SpeexResamplerState;
 
 namespace WebCore {
 
 // HRTFElevation contains all of the HRTFKernels (one left ear and one right ear per azimuth angle) for a particular elevation.
 
@@ -58,16 +59,18 @@ public:
     // The interpolated delays based on azimuthBlend: 0 -> 1 are returned in frameDelayL and frameDelayR.
     void getKernelsFromAzimuth(double azimuthBlend, unsigned azimuthIndex, HRTFKernel* &kernelL, HRTFKernel* &kernelR, double& frameDelayL, double& frameDelayR);
     
     // Total number of azimuths after interpolation.
     static const unsigned NumberOfTotalAzimuths;
 
     static size_t fftSizeForSampleRate(float sampleRate);
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     HRTFElevation(const HRTFElevation& other) MOZ_DELETE;
     void operator=(const HRTFElevation& other) MOZ_DELETE;
 
     HRTFElevation(HRTFKernelList *kernelListL, int elevation, float sampleRate)
         : m_elevationAngle(elevation)
         , m_sampleRate(sampleRate)
     {
--- a/content/media/webaudio/blink/HRTFKernel.h
+++ b/content/media/webaudio/blink/HRTFKernel.h
@@ -28,16 +28,17 @@
 
 #ifndef HRTFKernel_h
 #define HRTFKernel_h
 
 #include "nsAutoPtr.h"
 #include "nsAutoRef.h"
 #include "nsTArray.h"
 #include "mozilla/FFTBlock.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 using mozilla::FFTBlock;
 
 // HRTF stands for Head-Related Transfer Function.
 // HRTFKernel is a frequency-domain representation of an impulse-response used as part of the spatialized panning system.
 // For a given azimuth / elevation angle there will be one HRTFKernel for the left ear transfer function, and one for the right ear.
@@ -59,16 +60,23 @@ public:
     FFTBlock* fftFrame() { return m_fftFrame.get(); }
     
     size_t fftSize() const { return m_fftFrame->FFTSize(); }
     float frameDelay() const { return m_frameDelay; }
 
     float sampleRate() const { return m_sampleRate; }
     double nyquist() const { return 0.5 * sampleRate(); }
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+    {
+        size_t amount = aMallocSizeOf(this);
+        amount += m_fftFrame->SizeOfIncludingThis(aMallocSizeOf);
+        return amount;
+    }
+
 private:
     HRTFKernel(const HRTFKernel& other) MOZ_DELETE;
     void operator=(const HRTFKernel& other) MOZ_DELETE;
 
     // Note: this is destructive on the passed in |impulseResponse|.
     HRTFKernel(float* impulseResponse, size_t fftSize, float sampleRate);
     
     HRTFKernel(nsAutoPtr<FFTBlock> fftFrame, float frameDelay, float sampleRate)
--- a/content/media/webaudio/blink/HRTFPanner.cpp
+++ b/content/media/webaudio/blink/HRTFPanner.cpp
@@ -65,16 +65,37 @@ HRTFPanner::HRTFPanner(float sampleRate,
     m_tempR2.SetLength(RenderingQuantum);
 }
 
 HRTFPanner::~HRTFPanner()
 {
     MOZ_COUNT_DTOR(HRTFPanner);
 }
 
+size_t HRTFPanner::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+
+    if (m_databaseLoader) {
+        m_databaseLoader->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    amount += m_convolverL1.sizeOfExcludingThis(aMallocSizeOf);
+    amount += m_convolverR1.sizeOfExcludingThis(aMallocSizeOf);
+    amount += m_convolverL2.sizeOfExcludingThis(aMallocSizeOf);
+    amount += m_convolverR2.sizeOfExcludingThis(aMallocSizeOf);
+    amount += m_delayLine.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_tempL1.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_tempL2.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_tempR1.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_tempR2.SizeOfExcludingThis(aMallocSizeOf);
+
+    return amount;
+}
+
 void HRTFPanner::reset()
 {
     m_azimuthIndex1 = UninitializedAzimuth;
     m_azimuthIndex2 = UninitializedAzimuth;
     // m_elevation1 and m_elevation2 are initialized in pan()
     m_crossfadeSelection = CrossfadeSelection1;
     m_crossfadeX = 0.0f;
     m_crossfadeIncr = 0.0f;
--- a/content/media/webaudio/blink/HRTFPanner.h
+++ b/content/media/webaudio/blink/HRTFPanner.h
@@ -22,16 +22,17 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef HRTFPanner_h
 #define HRTFPanner_h
 
 #include "FFTConvolver.h"
 #include "DelayBuffer.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 struct AudioChunk;
 }
 
 namespace WebCore {
 
 class HRTFDatabaseLoader;
@@ -48,16 +49,18 @@ public:
     void reset();
 
     size_t fftSize() const { return m_convolverL1.fftSize(); }
 
     float sampleRate() const { return m_sampleRate; }
 
     int maxTailFrames() const;
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     // Given an azimuth angle in the range -180 -> +180, returns the corresponding azimuth index for the database,
     // and azimuthBlend which is an interpolation value from 0 -> 1.
     int calculateDesiredAzimuthIndexAndBlend(double azimuth, double& azimuthBlend);
 
     mozilla::RefPtr<HRTFDatabaseLoader> m_databaseLoader;
 
     float m_sampleRate;
--- a/content/media/webaudio/blink/PeriodicWave.cpp
+++ b/content/media/webaudio/blink/PeriodicWave.cpp
@@ -90,16 +90,30 @@ PeriodicWave::PeriodicWave(float sampleR
     , m_numberOfRanges(NumberOfRanges)
     , m_centsPerRange(CentsPerRange)
 {
     float nyquist = 0.5 * m_sampleRate;
     m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
     m_rateScale = m_periodicWaveSize / m_sampleRate;
 }
 
+size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+
+    amount += m_bandLimitedTables.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_bandLimitedTables.Length(); i++) {
+        if (m_bandLimitedTables[i]) {
+            amount += m_bandLimitedTables[i]->SizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    return amount;
+}
+
 void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
 {
     // Negative frequencies are allowed, in which case we alias
     // to the positive frequency.
     fundamentalFrequency = fabsf(fundamentalFrequency);
 
     // Calculate the pitch range.
     float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
--- a/content/media/webaudio/blink/PeriodicWave.h
+++ b/content/media/webaudio/blink/PeriodicWave.h
@@ -27,16 +27,17 @@
  */
 
 #ifndef PeriodicWave_h
 #define PeriodicWave_h
 
 #include "mozilla/dom/OscillatorNodeBinding.h"
 #include <nsAutoPtr.h>
 #include <nsTArray.h>
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 typedef nsTArray<float> AudioFloatArray;
 
 class PeriodicWave {
 public:
     static PeriodicWave* createSine(float sampleRate);
@@ -63,16 +64,18 @@ public:
 
     // Returns the scalar multiplier to the oscillator frequency to calculate
     // wave buffer phase increment.
     float rateScale() const { return m_rateScale; }
 
     unsigned periodicWaveSize() const { return m_periodicWaveSize; }
     float sampleRate() const { return m_sampleRate; }
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     explicit PeriodicWave(float sampleRate);
 
     void generateBasicWaveform(mozilla::dom::OscillatorType);
 
     float m_sampleRate;
     unsigned m_periodicWaveSize;
     unsigned m_numberOfRanges;
--- a/content/media/webaudio/blink/Reverb.cpp
+++ b/content/media/webaudio/blink/Reverb.cpp
@@ -100,16 +100,31 @@ Reverb::Reverb(ThreadSharedFloatArrayBuf
             }
         }
     }
 
     initialize(irChannels, impulseResponseBufferLength, renderSliceSize,
                maxFFTSize, numberOfChannels, useBackgroundThreads);
 }
 
+size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+    amount += m_convolvers.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_convolvers.Length(); i++) {
+        if (m_convolvers[i]) {
+            amount += m_convolvers[i]->sizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    amount += m_tempBuffer.SizeOfExcludingThis(aMallocSizeOf, false);
+    return amount;
+}
+
+
 void Reverb::initialize(const nsTArray<const float*>& impulseResponseBuffer,
                         size_t impulseResponseBufferLength, size_t renderSliceSize,
                         size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads)
 {
     m_impulseResponseLength = impulseResponseBufferLength;
 
     // The reverb can handle a mono impulse response and still do stereo processing
     size_t numResponseChannels = impulseResponseBuffer.Length();
--- a/content/media/webaudio/blink/Reverb.h
+++ b/content/media/webaudio/blink/Reverb.h
@@ -28,16 +28,17 @@
 
 #ifndef Reverb_h
 #define Reverb_h
 
 #include "ReverbConvolver.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "AudioSegment.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 class ThreadSharedFloatArrayBufferList;
 }
 
 namespace WebCore {
 
 class DirectConvolver;
@@ -53,16 +54,18 @@ public:
     Reverb(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate);
 
     void process(const mozilla::AudioChunk* sourceBus, mozilla::AudioChunk* destinationBus, size_t framesToProcess);
     void reset();
 
     size_t impulseResponseLength() const { return m_impulseResponseLength; }
     size_t latencyFrames() const;
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     void initialize(const nsTArray<const float*>& impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads);
 
     size_t m_impulseResponseLength;
 
     nsTArray<nsAutoPtr<ReverbConvolver> > m_convolvers;
 
     // For "True" stereo processing
--- a/content/media/webaudio/blink/ReverbAccumulationBuffer.h
+++ b/content/media/webaudio/blink/ReverbAccumulationBuffer.h
@@ -25,16 +25,17 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef ReverbAccumulationBuffer_h
 #define ReverbAccumulationBuffer_h
 
 #include "nsTArray.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 typedef nsTArray<float> AudioFloatArray;
 
 // ReverbAccumulationBuffer is a circular delay buffer with one client reading from it and multiple clients
 // writing/accumulating to it at different delay offsets from the read position.  The read operation will zero the memory
 // just read from the buffer, so it will be ready for accumulation the next time around.
@@ -53,16 +54,21 @@ public:
 
     size_t readIndex() const { return m_readIndex; }
     void updateReadIndex(int* readIndex, size_t numberOfFrames) const;
 
     size_t readTimeFrame() const { return m_readTimeFrame; }
 
     void reset();
 
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+    {
+        return m_buffer.SizeOfExcludingThis(aMallocSizeOf);
+    }
+
 private:
     AudioFloatArray m_buffer;
     size_t m_readIndex;
     size_t m_readTimeFrame; // for debugging (frame on continuous timeline)
 };
 
 } // namespace WebCore
 
--- a/content/media/webaudio/blink/ReverbConvolver.cpp
+++ b/content/media/webaudio/blink/ReverbConvolver.cpp
@@ -146,16 +146,45 @@ ReverbConvolver::~ReverbConvolver()
             m_moreInputBuffered = true;
             m_backgroundThreadCondition.Signal();
         }
 
         m_backgroundThread.Stop();
     }
 }
 
+size_t ReverbConvolver::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+    amount += m_stages.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_stages.Length(); i++) {
+        if (m_stages[i]) {
+            amount += m_stages[i]->sizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    amount += m_backgroundStages.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_backgroundStages.Length(); i++) {
+        if (m_backgroundStages[i]) {
+            amount += m_backgroundStages[i]->sizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    // NB: The buffer sizes are static, so even though they might be accessed
+    //     in another thread it's safe to measure them.
+    amount += m_accumulationBuffer.sizeOfExcludingThis(aMallocSizeOf);
+    amount += m_inputBuffer.sizeOfExcludingThis(aMallocSizeOf);
+
+    // Possible future measurements:
+    // - m_backgroundThread
+    // - m_backgroundThreadLock
+    // - m_backgroundThreadCondition
+    return amount;
+}
+
 void ReverbConvolver::backgroundThreadEntry()
 {
     while (!m_wantsToExit) {
         // Wait for realtime thread to give us more input
         m_moreInputBuffered = false;
         {
             AutoLock locker(m_backgroundThreadLock);
             while (!m_moreInputBuffered && !m_wantsToExit)
--- a/content/media/webaudio/blink/ReverbConvolver.h
+++ b/content/media/webaudio/blink/ReverbConvolver.h
@@ -27,16 +27,17 @@
  */
 
 #ifndef ReverbConvolver_h
 #define ReverbConvolver_h
 
 #include "ReverbAccumulationBuffer.h"
 #include "ReverbInputBuffer.h"
 #include "nsAutoPtr.h"
+#include "mozilla/MemoryReporting.h"
 #ifdef LOG
 #undef LOG
 #endif
 #include "base/condition_variable.h"
 #include "base/lock.h"
 #include "base/thread.h"
 
 namespace WebCore {
@@ -60,16 +61,18 @@ public:
     size_t impulseResponseLength() const { return m_impulseResponseLength; }
 
     ReverbInputBuffer* inputBuffer() { return &m_inputBuffer; }
 
     bool useBackgroundThreads() const { return m_useBackgroundThreads; }
     void backgroundThreadEntry();
 
     size_t latencyFrames() const;
+
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 private:
     nsTArray<nsAutoPtr<ReverbConvolverStage> > m_stages;
     nsTArray<nsAutoPtr<ReverbConvolverStage> > m_backgroundStages;
     size_t m_impulseResponseLength;
 
     ReverbAccumulationBuffer m_accumulationBuffer;
 
     // One or more background threads read from this input buffer which is fed from the realtime thread.
--- a/content/media/webaudio/blink/ReverbConvolverStage.cpp
+++ b/content/media/webaudio/blink/ReverbConvolverStage.cpp
@@ -82,16 +82,39 @@ ReverbConvolverStage::ReverbConvolverSta
     m_framesProcessed = 0; // total frames processed so far
 
     size_t delayBufferSize = m_preDelayLength < fftSize ? fftSize : m_preDelayLength;
     delayBufferSize = delayBufferSize < renderSliceSize ? renderSliceSize : delayBufferSize;
     m_preDelayBuffer.SetLength(delayBufferSize);
     PodZero(m_preDelayBuffer.Elements(), m_preDelayBuffer.Length());
 }
 
+size_t ReverbConvolverStage::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+
+    if (m_fftKernel) {
+        amount += m_fftKernel->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    if (m_fftConvolver) {
+        amount += m_fftConvolver->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    amount += m_preDelayBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_temporaryBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_directKernel.SizeOfExcludingThis(aMallocSizeOf);
+
+    if (m_directConvolver) {
+        amount += m_directConvolver->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+}
+
 void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver, size_t framesToProcess)
 {
     ReverbInputBuffer* inputBuffer = convolver->inputBuffer();
     float* source = inputBuffer->directReadFrom(&m_inputReadIndex, framesToProcess);
     process(source, framesToProcess);
 }
 
 void ReverbConvolverStage::process(const float* source, size_t framesToProcess)
--- a/content/media/webaudio/blink/ReverbConvolverStage.h
+++ b/content/media/webaudio/blink/ReverbConvolverStage.h
@@ -29,16 +29,17 @@
 #ifndef ReverbConvolverStage_h
 #define ReverbConvolverStage_h
 
 #include "DirectConvolver.h"
 #include "FFTConvolver.h"
 
 #include "nsTArray.h"
 #include "mozilla/FFTBlock.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 using mozilla::FFTBlock;
 
 class ReverbAccumulationBuffer;
 class ReverbConvolver;
 
@@ -55,16 +56,18 @@ public:
 
     void processInBackground(ReverbConvolver* convolver, size_t framesToProcess);
 
     void reset();
 
     // Useful for background processing
     int inputReadIndex() const { return m_inputReadIndex; }
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     nsAutoPtr<FFTBlock> m_fftKernel;
     nsAutoPtr<FFTConvolver> m_fftConvolver;
 
     nsTArray<float> m_preDelayBuffer;
 
     ReverbAccumulationBuffer* m_accumulationBuffer;
     int m_accumulationReadIndex;
--- a/content/media/webaudio/blink/ReverbInputBuffer.h
+++ b/content/media/webaudio/blink/ReverbInputBuffer.h
@@ -25,16 +25,17 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef ReverbInputBuffer_h
 #define ReverbInputBuffer_h
 
 #include "nsTArray.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 // ReverbInputBuffer is used to buffer input samples for deferred processing by the background threads.
 class ReverbInputBuffer {
 public:
     ReverbInputBuffer(size_t length);
 
@@ -49,16 +50,22 @@ public:
     // The individual background threads read here (and hope that they can keep up with the buffer writing).
     // readIndex is updated with the next readIndex to read from...
     // The assumption is that the buffer's length is evenly divisible by numberOfFrames.
     // FIXME: remove numberOfFrames restriction...
     float* directReadFrom(int* readIndex, size_t numberOfFrames);
 
     void reset();
 
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+    {
+        return m_buffer.SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+
 private:
     nsTArray<float> m_buffer;
     size_t m_writeIndex;
 };
 
 } // namespace WebCore
 
 #endif // ReverbInputBuffer_h